django-rest-framework/tests/test_serializer.py
Carl Meyer 140f8620ae Set up serializer fields lazily on-demand.
This avoids AppRegistryNotReady problems in Django 1.7 with nested serializers,
which are instantiated at import time, possibly before Django's app registry is
fully populated.
2014-10-17 09:50:57 -06:00

2007 lines
69 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.db.models.fields import BLANK_CHOICE_DASH
from django.test import TestCase
from django.utils import unittest
from django.utils.datastructures import MultiValueDict
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers, fields, relations
from tests.models import (
HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel,
DefaultValueModel, ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo,
RESTFrameworkModel, ForeignKeySource
)
from tests.models import BasicModelSerializer
import datetime
import pickle
try:
import PIL
except:
PIL = None
if PIL is not None:
class AMOAFModel(RESTFrameworkModel):
char_field = models.CharField(max_length=1024, blank=True)
comma_separated_integer_field = models.CommaSeparatedIntegerField(max_length=1024, blank=True)
decimal_field = models.DecimalField(max_digits=64, decimal_places=32, blank=True)
email_field = models.EmailField(max_length=1024, blank=True)
file_field = models.FileField(upload_to='test', max_length=1024, blank=True)
image_field = models.ImageField(upload_to='test', max_length=1024, blank=True)
slug_field = models.SlugField(max_length=1024, blank=True)
url_field = models.URLField(max_length=1024, blank=True)
nullable_char_field = models.CharField(max_length=1024, blank=True, null=True)
class DVOAFModel(RESTFrameworkModel):
positive_integer_field = models.PositiveIntegerField(blank=True)
positive_small_integer_field = models.PositiveSmallIntegerField(blank=True)
email_field = models.EmailField(blank=True)
file_field = models.FileField(upload_to='test', blank=True)
image_field = models.ImageField(upload_to='test', blank=True)
slug_field = models.SlugField(blank=True)
url_field = models.URLField(blank=True)
class SubComment(object):
def __init__(self, sub_comment):
self.sub_comment = sub_comment
class Comment(object):
def __init__(self, email, content, created):
self.email = email
self.content = content
self.created = created or datetime.datetime.now()
def __eq__(self, other):
return all([getattr(self, attr) == getattr(other, attr)
for attr in ('email', 'content', 'created')])
def get_sub_comment(self):
sub_comment = SubComment('And Merry Christmas!')
return sub_comment
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=1000)
created = serializers.DateTimeField()
sub_comment = serializers.Field(source='get_sub_comment.sub_comment')
def restore_object(self, data, instance=None):
if instance is None:
return Comment(**data)
for key, val in data.items():
setattr(instance, key, val)
return instance
class NamesSerializer(serializers.Serializer):
first = serializers.CharField()
last = serializers.CharField(required=False, default='')
initials = serializers.CharField(required=False, default='')
class PersonIdentifierSerializer(serializers.Serializer):
ssn = serializers.CharField()
names = NamesSerializer(source='names', required=False)
class BookSerializer(serializers.ModelSerializer):
isbn = serializers.RegexField(regex=r'^[0-9]{13}$', error_messages={'invalid': 'isbn has to be exact 13 numbers'})
class Meta:
model = Book
class ActionItemSerializer(serializers.ModelSerializer):
class Meta:
model = ActionItem
class ActionItemSerializerOptionalFields(serializers.ModelSerializer):
"""
Intended to test that fields with `required=False` are excluded from validation.
"""
title = serializers.CharField(required=False)
class Meta:
model = ActionItem
fields = ('title',)
class ActionItemSerializerCustomRestore(serializers.ModelSerializer):
class Meta:
model = ActionItem
def restore_object(self, data, instance=None):
if instance is None:
return ActionItem(**data)
for key, val in data.items():
setattr(instance, key, val)
return instance
class PersonSerializer(serializers.ModelSerializer):
info = serializers.Field(source='info')
class Meta:
model = Person
fields = ('name', 'age', 'info')
read_only_fields = ('age',)
class NestedSerializer(serializers.Serializer):
info = serializers.Field()
class ModelSerializerWithNestedSerializer(serializers.ModelSerializer):
nested = NestedSerializer(source='*')
class Meta:
model = Person
class NestedSerializerWithRenamedField(serializers.Serializer):
renamed_info = serializers.Field(source='info')
class ModelSerializerWithNestedSerializerWithRenamedField(serializers.ModelSerializer):
nested = NestedSerializerWithRenamedField(source='*')
class Meta:
model = Person
class PersonSerializerInvalidReadOnly(serializers.ModelSerializer):
"""
Testing for #652.
"""
info = serializers.Field(source='info')
class Meta:
model = Person
fields = ('name', 'age', 'info')
read_only_fields = ('age', 'info')
class AlbumsSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = ['title', 'ref'] # lists are also valid options
class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = HasPositiveIntegerAsChoice
fields = ['some_integer']
class ForeignKeySourceSerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKeySource
class HyperlinkedForeignKeySourceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ForeignKeySource
class BasicTests(TestCase):
def setUp(self):
self.comment = Comment(
'tom@example.com',
'Happy new year!',
datetime.datetime(2012, 1, 1)
)
self.actionitem = ActionItem(title='Some to do item',)
self.data = {
'email': 'tom@example.com',
'content': 'Happy new year!',
'created': datetime.datetime(2012, 1, 1),
'sub_comment': 'This wont change'
}
self.expected = {
'email': 'tom@example.com',
'content': 'Happy new year!',
'created': datetime.datetime(2012, 1, 1),
'sub_comment': 'And Merry Christmas!'
}
self.person_data = {'name': 'dwight', 'age': 35}
self.person = Person(**self.person_data)
self.person.save()
def test_empty(self):
serializer = CommentSerializer()
expected = {
'email': '',
'content': '',
'created': None
}
self.assertEqual(serializer.data, expected)
def test_retrieve(self):
serializer = CommentSerializer(self.comment)
self.assertEqual(serializer.data, self.expected)
def test_create(self):
serializer = CommentSerializer(data=self.data)
expected = self.comment
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected)
self.assertFalse(serializer.object is expected)
self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!')
def test_create_nested(self):
"""Test a serializer with nested data."""
names = {'first': 'John', 'last': 'Doe', 'initials': 'jd'}
data = {'ssn': '1234567890', 'names': names}
serializer = PersonIdentifierSerializer(data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, data)
self.assertFalse(serializer.object is data)
self.assertEqual(serializer.data['names'], names)
def test_create_partial_nested(self):
"""Test a serializer with nested data which has missing fields."""
names = {'first': 'John'}
data = {'ssn': '1234567890', 'names': names}
serializer = PersonIdentifierSerializer(data=data)
expected_names = {'first': 'John', 'last': '', 'initials': ''}
data['names'] = expected_names
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, data)
self.assertFalse(serializer.object is expected_names)
self.assertEqual(serializer.data['names'], expected_names)
def test_null_nested(self):
"""Test a serializer with a nonexistent nested field"""
data = {'ssn': '1234567890'}
serializer = PersonIdentifierSerializer(data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, data)
self.assertFalse(serializer.object is data)
expected = {'ssn': '1234567890', 'names': None}
self.assertEqual(serializer.data, expected)
def test_update(self):
serializer = CommentSerializer(self.comment, data=self.data)
expected = self.comment
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected)
self.assertTrue(serializer.object is expected)
self.assertEqual(serializer.data['sub_comment'], 'And Merry Christmas!')
def test_partial_update(self):
msg = 'Merry New Year!'
partial_data = {'content': msg}
serializer = CommentSerializer(self.comment, data=partial_data)
self.assertEqual(serializer.is_valid(), False)
serializer = CommentSerializer(self.comment, data=partial_data, partial=True)
expected = self.comment
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.object, expected)
self.assertTrue(serializer.object is expected)
self.assertEqual(serializer.data['content'], msg)
def test_model_fields_as_expected(self):
"""
Make sure that the fields returned are the same as defined
in the Meta data
"""
serializer = PersonSerializer(self.person)
self.assertEqual(
set(serializer.data.keys()),
set(['name', 'age', 'info'])
)
def test_field_with_dictionary(self):
"""
Make sure that dictionaries from fields are left intact
"""
serializer = PersonSerializer(self.person)
expected = self.person_data
self.assertEqual(serializer.data['info'], expected)
def test_read_only_fields(self):
"""
Attempting to update fields set as read_only should have no effect.
"""
serializer = PersonSerializer(self.person, data={'name': 'dwight', 'age': 99})
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(serializer.errors, {})
# Assert age is unchanged (35)
self.assertEqual(instance.age, self.person_data['age'])
def test_invalid_read_only_fields(self):
"""
Regression test for #652.
"""
serializer = PersonSerializerInvalidReadOnly()
with self.assertRaises(AssertionError):
serializer.fields
def test_serializer_data_is_cleared_on_save(self):
"""
Check _data attribute is cleared on `save()`
Regression test for #1116
 id field is not populated if `data` is accessed prior to `save()`
"""
serializer = ActionItemSerializer(self.actionitem)
self.assertIsNone(serializer.data.get('id', None), 'New instance. `id` should not be set.')
serializer.save()
self.assertIsNotNone(serializer.data.get('id', None), 'Model is saved. `id` should be set.')
def test_fields_marked_as_not_required_are_excluded_from_validation(self):
"""
Check that fields with `required=False` are included in list of exclusions.
"""
serializer = ActionItemSerializerOptionalFields(self.actionitem)
exclusions = serializer.get_validation_exclusions()
self.assertTrue('title' in exclusions, '`title` field was marked `required=False` and should be excluded')
class DictStyleSerializer(serializers.Serializer):
"""
Note that we don't have any `restore_object` method, so the default
case of simply returning a dict will apply.
"""
email = serializers.EmailField()
class DictStyleSerializerTests(TestCase):
def test_dict_style_deserialize(self):
"""
Ensure serializers can deserialize into a dict.
"""
data = {'email': 'foo@example.com'}
serializer = DictStyleSerializer(data=data)
self.assertTrue(serializer.is_valid())
self.assertEqual(serializer.data, data)
def test_dict_style_serialize(self):
"""
Ensure serializers can serialize dict objects.
"""
data = {'email': 'foo@example.com'}
serializer = DictStyleSerializer(data)
self.assertEqual(serializer.data, data)
class ValidationTests(TestCase):
def setUp(self):
self.comment = Comment(
'tom@example.com',
'Happy new year!',
datetime.datetime(2012, 1, 1)
)
self.data = {
'email': 'tom@example.com',
'content': 'x' * 1001,
'created': datetime.datetime(2012, 1, 1)
}
self.actionitem = ActionItem(title='Some to do item',)
def test_create(self):
serializer = CommentSerializer(data=self.data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})
def test_update(self):
serializer = CommentSerializer(self.comment, data=self.data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'content': ['Ensure this value has at most 1000 characters (it has 1001).']})
def test_update_missing_field(self):
data = {
'content': 'xxx',
'created': datetime.datetime(2012, 1, 1)
}
serializer = CommentSerializer(self.comment, data=data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'email': ['This field is required.']})
def test_missing_bool_with_default(self):
"""Make sure that a boolean value with a 'False' value is not
mistaken for not having a default."""
data = {
'title': 'Some action item',
# No 'done' value.
}
serializer = ActionItemSerializer(self.actionitem, data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.errors, {})
def test_cross_field_validation(self):
class CommentSerializerWithCrossFieldValidator(CommentSerializer):
def validate(self, attrs):
if attrs["email"] not in attrs["content"]:
raise serializers.ValidationError("Email address not in content")
return attrs
data = {
'email': 'tom@example.com',
'content': 'A comment from tom@example.com',
'created': datetime.datetime(2012, 1, 1)
}
serializer = CommentSerializerWithCrossFieldValidator(data=data)
self.assertTrue(serializer.is_valid())
data['content'] = 'A comment from foo@bar.com'
serializer = CommentSerializerWithCrossFieldValidator(data=data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'non_field_errors': ['Email address not in content']})
def test_null_is_true_fields(self):
"""
Omitting a value for null-field should validate.
"""
serializer = PersonSerializer(data={'name': 'marko'})
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.errors, {})
def test_modelserializer_max_length_exceeded(self):
data = {
'title': 'x' * 201,
}
serializer = ActionItemSerializer(data=data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})
def test_modelserializer_max_length_exceeded_with_custom_restore(self):
"""
When overriding ModelSerializer.restore_object, validation tests should still apply.
Regression test for #623.
https://github.com/tomchristie/django-rest-framework/pull/623
"""
data = {
'title': 'x' * 201,
}
serializer = ActionItemSerializerCustomRestore(data=data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})
def test_default_modelfield_max_length_exceeded(self):
data = {
'title': 'Testing "info" field...',
'info': 'x' * 13,
}
serializer = ActionItemSerializer(data=data)
self.assertEqual(serializer.is_valid(), False)
self.assertEqual(serializer.errors, {'info': ['Ensure this value has at most 12 characters (it has 13).']})
def test_datetime_validation_failure(self):
"""
Test DateTimeField validation errors on non-str values.
Regression test for #669.
https://github.com/tomchristie/django-rest-framework/issues/669
"""
data = self.data
data['created'] = 0
serializer = CommentSerializer(data=data)
self.assertEqual(serializer.is_valid(), False)
self.assertIn('created', serializer.errors)
def test_missing_model_field_exception_msg(self):
"""
Assert that a meaningful exception message is outputted when the model
field is missing (e.g. when mistyping ``model``).
"""
class BrokenModelSerializer(serializers.ModelSerializer):
class Meta:
fields = ['some_field']
try:
BrokenModelSerializer()
except AssertionError as e:
self.assertEqual(e.args[0], "Serializer class 'BrokenModelSerializer' is missing 'model' Meta option")
except:
self.fail('Wrong exception type thrown.')
def test_writable_star_source_on_nested_serializer(self):
"""
Assert that a nested serializer instantiated with source='*' correctly
expands the data into the outer serializer.
"""
serializer = ModelSerializerWithNestedSerializer(data={
'name': 'marko',
'nested': {'info': 'hi'}},
)
self.assertEqual(serializer.is_valid(), True)
def test_writable_star_source_on_nested_serializer_with_parent_object(self):
class TitleSerializer(serializers.Serializer):
title = serializers.WritableField(source='title')
class AlbumSerializer(serializers.ModelSerializer):
nested = TitleSerializer(source='*')
class Meta:
model = Album
fields = ('nested',)
class PhotoSerializer(serializers.ModelSerializer):
album = AlbumSerializer(source='album')
class Meta:
model = Photo
fields = ('album', )
photo = Photo(album=Album())
data = {'album': {'nested': {'title': 'test'}}}
serializer = PhotoSerializer(photo, data=data)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data, data)
def test_writable_star_source_with_inner_source_fields(self):
"""
Tests that a serializer with source="*" correctly expands the
it's fields into the outer serializer even if they have their
own 'source' parameters.
"""
serializer = ModelSerializerWithNestedSerializerWithRenamedField(data={
'name': 'marko',
'nested': {'renamed_info': 'hi'}},
)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.errors, {})
class CustomValidationTests(TestCase):
class CommentSerializerWithFieldValidator(CommentSerializer):
def validate_email(self, attrs, source):
attrs[source]
return attrs
def validate_content(self, attrs, source):
value = attrs[source]
if "test" not in value:
raise serializers.ValidationError("Test not in value")
return attrs
def test_field_validation(self):
data = {
'email': 'tom@example.com',
'content': 'A test comment',
'created': datetime.datetime(2012, 1, 1)
}
serializer = self.CommentSerializerWithFieldValidator(data=data)
self.assertTrue(serializer.is_valid())
data['content'] = 'This should not validate'
serializer = self.CommentSerializerWithFieldValidator(data=data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'content': ['Test not in value']})
def test_missing_data(self):
"""
Make sure that validate_content isn't called if the field is missing
"""
incomplete_data = {
'email': 'tom@example.com',
'created': datetime.datetime(2012, 1, 1)
}
serializer = self.CommentSerializerWithFieldValidator(data=incomplete_data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'content': ['This field is required.']})
def test_wrong_data(self):
"""
Make sure that validate_content isn't called if the field input is wrong
"""
wrong_data = {
'email': 'not an email',
'content': 'A test comment',
'created': datetime.datetime(2012, 1, 1)
}
serializer = self.CommentSerializerWithFieldValidator(data=wrong_data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'email': ['Enter a valid email address.']})
def test_partial_update(self):
"""
Make sure that validate_email isn't called when partial=True and email
isn't found in data.
"""
initial_data = {
'email': 'tom@example.com',
'content': 'A test comment',
'created': datetime.datetime(2012, 1, 1)
}
serializer = self.CommentSerializerWithFieldValidator(data=initial_data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.object
new_content = 'An *updated* test comment'
partial_data = {
'content': new_content
}
serializer = self.CommentSerializerWithFieldValidator(instance=instance,
data=partial_data,
partial=True)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.object
self.assertEqual(instance.content, new_content)
class PositiveIntegerAsChoiceTests(TestCase):
def test_positive_integer_in_json_is_correctly_parsed(self):
data = {'some_integer': 1}
serializer = PositiveIntegerAsChoiceSerializer(data=data)
self.assertEqual(serializer.is_valid(), True)
class ModelValidationTests(TestCase):
def test_validate_unique(self):
"""
Just check if serializers.ModelSerializer handles unique checks via .full_clean()
"""
serializer = AlbumsSerializer(data={'title': 'a', 'ref': '1'})
serializer.is_valid()
serializer.save()
second_serializer = AlbumsSerializer(data={'title': 'a'})
self.assertFalse(second_serializer.is_valid())
self.assertEqual(second_serializer.errors, {'title': ['Album with this Title already exists.']})
third_serializer = AlbumsSerializer(data=[{'title': 'b', 'ref': '1'}, {'title': 'c'}], many=True)
self.assertFalse(third_serializer.is_valid())
self.assertEqual(third_serializer.errors, [{'ref': ['Album with this Ref already exists.']}, {}])
def test_foreign_key_is_null_with_partial(self):
"""
Test ModelSerializer validation with partial=True
Specifically test that a null foreign key does not pass validation
"""
album = Album(title='test')
album.save()
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
photo_serializer = PhotoSerializer(data={'description': 'test', 'album': album.pk})
self.assertTrue(photo_serializer.is_valid())
photo = photo_serializer.save()
# Updating only the album (foreign key)
photo_serializer = PhotoSerializer(instance=photo, data={'album': ''}, partial=True)
self.assertFalse(photo_serializer.is_valid())
self.assertTrue('album' in photo_serializer.errors)
self.assertEqual(photo_serializer.errors['album'], [photo_serializer.error_messages['required']])
def test_foreign_key_with_partial(self):
"""
Test ModelSerializer validation with partial=True
Specifically test foreign key validation.
"""
album = Album(title='test')
album.save()
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
photo_serializer = PhotoSerializer(data={'description': 'test', 'album': album.pk})
self.assertTrue(photo_serializer.is_valid())
photo = photo_serializer.save()
# Updating only the album (foreign key)
photo_serializer = PhotoSerializer(instance=photo, data={'album': album.pk}, partial=True)
self.assertTrue(photo_serializer.is_valid())
self.assertTrue(photo_serializer.save())
# Updating only the description
photo_serializer = PhotoSerializer(instance=photo,
data={'description': 'new'},
partial=True)
self.assertTrue(photo_serializer.is_valid())
self.assertTrue(photo_serializer.save())
class RegexValidationTest(TestCase):
def test_create_failed(self):
serializer = BookSerializer(data={'isbn': '1234567890'})
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
serializer = BookSerializer(data={'isbn': '12345678901234'})
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
serializer = BookSerializer(data={'isbn': 'abcdefghijklm'})
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'isbn': ['isbn has to be exact 13 numbers']})
def test_create_success(self):
serializer = BookSerializer(data={'isbn': '1234567890123'})
self.assertTrue(serializer.is_valid())
class MetadataTests(TestCase):
def test_empty(self):
serializer = CommentSerializer()
expected = {
'email': serializers.CharField,
'content': serializers.CharField,
'created': serializers.DateTimeField
}
for field_name, field in expected.items():
self.assertTrue(isinstance(serializer.data.fields[field_name], field))
class ManyToManyTests(TestCase):
def setUp(self):
class ManyToManySerializer(serializers.ModelSerializer):
class Meta:
model = ManyToManyModel
self.serializer_class = ManyToManySerializer
# An anchor instance to use for the relationship
self.anchor = Anchor()
self.anchor.save()
# A model instance with a many to many relationship to the anchor
self.instance = ManyToManyModel()
self.instance.save()
self.instance.rel.add(self.anchor)
# A serialized representation of the model instance
self.data = {'id': 1, 'rel': [self.anchor.id]}
def test_retrieve(self):
"""
Serialize an instance of a model with a ManyToMany relationship.
"""
serializer = self.serializer_class(instance=self.instance)
expected = self.data
self.assertEqual(serializer.data, expected)
def test_create(self):
"""
Create an instance of a model with a ManyToMany relationship.
"""
data = {'rel': [self.anchor.id]}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ManyToManyModel.objects.all()), 2)
self.assertEqual(instance.pk, 2)
self.assertEqual(list(instance.rel.all()), [self.anchor])
def test_update(self):
"""
Update an instance of a model with a ManyToMany relationship.
"""
new_anchor = Anchor()
new_anchor.save()
data = {'rel': [self.anchor.id, new_anchor.id]}
serializer = self.serializer_class(self.instance, data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ManyToManyModel.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(list(instance.rel.all()), [self.anchor, new_anchor])
def test_create_empty_relationship(self):
"""
Create an instance of a model with a ManyToMany relationship,
containing no items.
"""
data = {'rel': []}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ManyToManyModel.objects.all()), 2)
self.assertEqual(instance.pk, 2)
self.assertEqual(list(instance.rel.all()), [])
def test_update_empty_relationship(self):
"""
Update an instance of a model with a ManyToMany relationship,
containing no items.
"""
new_anchor = Anchor()
new_anchor.save()
data = {'rel': []}
serializer = self.serializer_class(self.instance, data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ManyToManyModel.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(list(instance.rel.all()), [])
def test_create_empty_relationship_flat_data(self):
"""
Create an instance of a model with a ManyToMany relationship,
containing no items, using a representation that does not support
lists (eg form data).
"""
data = MultiValueDict()
data.setlist('rel', [''])
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ManyToManyModel.objects.all()), 2)
self.assertEqual(instance.pk, 2)
self.assertEqual(list(instance.rel.all()), [])
class ReadOnlyManyToManyTests(TestCase):
def setUp(self):
class ReadOnlyManyToManySerializer(serializers.ModelSerializer):
rel = serializers.RelatedField(many=True, read_only=True)
class Meta:
model = ReadOnlyManyToManyModel
self.serializer_class = ReadOnlyManyToManySerializer
# An anchor instance to use for the relationship
self.anchor = Anchor()
self.anchor.save()
# A model instance with a many to many relationship to the anchor
self.instance = ReadOnlyManyToManyModel()
self.instance.save()
self.instance.rel.add(self.anchor)
# A serialized representation of the model instance
self.data = {'rel': [self.anchor.id], 'id': 1, 'text': 'anchor'}
def test_update(self):
"""
Attempt to update an instance of a model with a ManyToMany
relationship. Not updated due to read_only=True
"""
new_anchor = Anchor()
new_anchor.save()
data = {'rel': [self.anchor.id, new_anchor.id]}
serializer = self.serializer_class(self.instance, data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1)
self.assertEqual(instance.pk, 1)
# rel is still as original (1 entry)
self.assertEqual(list(instance.rel.all()), [self.anchor])
def test_update_without_relationship(self):
"""
Attempt to update an instance of a model where many to ManyToMany
relationship is not supplied. Not updated due to read_only=True
"""
new_anchor = Anchor()
new_anchor.save()
data = {}
serializer = self.serializer_class(self.instance, data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(ReadOnlyManyToManyModel.objects.all()), 1)
self.assertEqual(instance.pk, 1)
# rel is still as original (1 entry)
self.assertEqual(list(instance.rel.all()), [self.anchor])
class DefaultValueTests(TestCase):
def setUp(self):
class DefaultValueSerializer(serializers.ModelSerializer):
class Meta:
model = DefaultValueModel
self.serializer_class = DefaultValueSerializer
self.objects = DefaultValueModel.objects
def test_create_using_default(self):
data = {}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(self.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(instance.text, 'foobar')
def test_create_overriding_default(self):
data = {'text': 'overridden'}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(self.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(instance.text, 'overridden')
def test_partial_update_default(self):
""" Regression test for issue #532 """
data = {'text': 'overridden'}
serializer = self.serializer_class(data=data, partial=True)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
data = {'extra': 'extra_value'}
serializer = self.serializer_class(instance=instance, data=data, partial=True)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(instance.extra, 'extra_value')
self.assertEqual(instance.text, 'overridden')
class WritableFieldDefaultValueTests(TestCase):
def setUp(self):
self.expected = {'default': 'value'}
self.create_field = fields.WritableField
def test_get_default_value_with_noncallable(self):
field = self.create_field(default=self.expected)
got = field.get_default_value()
self.assertEqual(got, self.expected)
def test_get_default_value_with_callable(self):
field = self.create_field(default=lambda: self.expected)
got = field.get_default_value()
self.assertEqual(got, self.expected)
def test_get_default_value_when_not_required(self):
field = self.create_field(default=self.expected, required=False)
got = field.get_default_value()
self.assertEqual(got, self.expected)
def test_get_default_value_returns_None(self):
field = self.create_field()
got = field.get_default_value()
self.assertIsNone(got)
def test_get_default_value_returns_non_True_values(self):
values = [None, '', False, 0, [], (), {}] # values that assumed as 'False' in the 'if' clause
for expected in values:
field = self.create_field(default=expected)
got = field.get_default_value()
self.assertEqual(got, expected)
class RelatedFieldDefaultValueTests(WritableFieldDefaultValueTests):
def setUp(self):
self.expected = {'foo': 'bar'}
self.create_field = relations.RelatedField
def test_get_default_value_returns_empty_list(self):
field = self.create_field(many=True)
got = field.get_default_value()
self.assertListEqual(got, [])
def test_get_default_value_returns_expected(self):
expected = [1, 2, 3]
field = self.create_field(many=True, default=expected)
got = field.get_default_value()
self.assertListEqual(got, expected)
class CallableDefaultValueTests(TestCase):
def setUp(self):
class CallableDefaultValueSerializer(serializers.ModelSerializer):
class Meta:
model = CallableDefaultValueModel
self.serializer_class = CallableDefaultValueSerializer
self.objects = CallableDefaultValueModel.objects
def test_create_using_default(self):
data = {}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(self.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(instance.text, 'foobar')
def test_create_overriding_default(self):
data = {'text': 'overridden'}
serializer = self.serializer_class(data=data)
self.assertEqual(serializer.is_valid(), True)
instance = serializer.save()
self.assertEqual(len(self.objects.all()), 1)
self.assertEqual(instance.pk, 1)
self.assertEqual(instance.text, 'overridden')
class ManyRelatedTests(TestCase):
def test_reverse_relations(self):
post = BlogPost.objects.create(title="Test blog post")
post.blogpostcomment_set.create(text="I hate this blog post")
post.blogpostcomment_set.create(text="I love this blog post")
class BlogPostCommentSerializer(serializers.Serializer):
text = serializers.CharField()
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField()
comments = BlogPostCommentSerializer(source='blogpostcomment_set')
serializer = BlogPostSerializer(instance=post)
expected = {
'title': 'Test blog post',
'comments': [
{'text': 'I hate this blog post'},
{'text': 'I love this blog post'}
]
}
self.assertEqual(serializer.data, expected)
def test_include_reverse_relations(self):
post = BlogPost.objects.create(title="Test blog post")
post.blogpostcomment_set.create(text="I hate this blog post")
post.blogpostcomment_set.create(text="I love this blog post")
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('id', 'title', 'blogpostcomment_set')
serializer = BlogPostSerializer(instance=post)
expected = {
'id': 1, 'title': 'Test blog post', 'blogpostcomment_set': [1, 2]
}
self.assertEqual(serializer.data, expected)
def test_depth_include_reverse_relations(self):
post = BlogPost.objects.create(title="Test blog post")
post.blogpostcomment_set.create(text="I hate this blog post")
post.blogpostcomment_set.create(text="I love this blog post")
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('id', 'title', 'blogpostcomment_set')
depth = 1
serializer = BlogPostSerializer(instance=post)
expected = {
'id': 1, 'title': 'Test blog post',
'blogpostcomment_set': [
{'id': 1, 'text': 'I hate this blog post', 'blog_post': 1},
{'id': 2, 'text': 'I love this blog post', 'blog_post': 1}
]
}
self.assertEqual(serializer.data, expected)
def test_callable_source(self):
post = BlogPost.objects.create(title="Test blog post")
post.blogpostcomment_set.create(text="I love this blog post")
class BlogPostCommentSerializer(serializers.Serializer):
text = serializers.CharField()
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField()
first_comment = BlogPostCommentSerializer(source='get_first_comment')
serializer = BlogPostSerializer(post)
expected = {
'title': 'Test blog post',
'first_comment': {'text': 'I love this blog post'}
}
self.assertEqual(serializer.data, expected)
class RelatedTraversalTest(TestCase):
def test_nested_traversal(self):
"""
Source argument should support dotted.source notation.
"""
user = Person.objects.create(name="django")
post = BlogPost.objects.create(title="Test blog post", writer=user)
post.blogpostcomment_set.create(text="I love this blog post")
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ("name", "age")
class BlogPostCommentSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPostComment
fields = ("text", "post_owner")
text = serializers.CharField()
post_owner = PersonSerializer(source='blog_post.writer')
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField()
comments = BlogPostCommentSerializer(source='blogpostcomment_set')
serializer = BlogPostSerializer(instance=post)
expected = {
'title': 'Test blog post',
'comments': [{
'text': 'I love this blog post',
'post_owner': {
"name": "django",
"age": None
}
}]
}
self.assertEqual(serializer.data, expected)
def test_nested_traversal_with_none(self):
"""
If a component of the dotted.source is None, return None for the field.
"""
from tests.models import NullableForeignKeySource
instance = NullableForeignKeySource.objects.create(name='Source with null FK')
class NullableSourceSerializer(serializers.Serializer):
target_name = serializers.Field(source='target.name')
serializer = NullableSourceSerializer(instance=instance)
expected = {
'target_name': None,
}
self.assertEqual(serializer.data, expected)
class SerializerMethodFieldTests(TestCase):
def setUp(self):
class BoopSerializer(serializers.Serializer):
beep = serializers.SerializerMethodField('get_beep')
boop = serializers.Field()
boop_count = serializers.SerializerMethodField('get_boop_count')
def get_beep(self, obj):
return 'hello!'
def get_boop_count(self, obj):
return len(obj.boop)
self.serializer_class = BoopSerializer
def test_serializer_method_field(self):
class MyModel(object):
boop = ['a', 'b', 'c']
source_data = MyModel()
serializer = self.serializer_class(source_data)
expected = {
'beep': 'hello!',
'boop': ['a', 'b', 'c'],
'boop_count': 3,
}
self.assertEqual(serializer.data, expected)
# Test for issue #324
class BlankFieldTests(TestCase):
def setUp(self):
class BlankFieldModelSerializer(serializers.ModelSerializer):
class Meta:
model = BlankFieldModel
class BlankFieldSerializer(serializers.Serializer):
title = serializers.CharField(required=False)
class NotBlankFieldModelSerializer(serializers.ModelSerializer):
class Meta:
model = BasicModel
class NotBlankFieldSerializer(serializers.Serializer):
title = serializers.CharField()
self.model_serializer_class = BlankFieldModelSerializer
self.serializer_class = BlankFieldSerializer
self.not_blank_model_serializer_class = NotBlankFieldModelSerializer
self.not_blank_serializer_class = NotBlankFieldSerializer
self.data = {'title': ''}
def test_create_blank_field(self):
serializer = self.serializer_class(data=self.data)
self.assertEqual(serializer.is_valid(), True)
def test_create_model_blank_field(self):
serializer = self.model_serializer_class(data=self.data)
self.assertEqual(serializer.is_valid(), True)
def test_create_model_null_field(self):
serializer = self.model_serializer_class(data={'title': None})
self.assertEqual(serializer.is_valid(), True)
serializer.save()
self.assertIsNot(serializer.object.pk, None)
self.assertEqual(serializer.object.title, '')
def test_create_not_blank_field(self):
"""
Test to ensure blank data in a field not marked as blank=True
is considered invalid in a non-model serializer
"""
serializer = self.not_blank_serializer_class(data=self.data)
self.assertEqual(serializer.is_valid(), False)
def test_create_model_not_blank_field(self):
"""
Test to ensure blank data in a field not marked as blank=True
is considered invalid in a model serializer
"""
serializer = self.not_blank_model_serializer_class(data=self.data)
self.assertEqual(serializer.is_valid(), False)
def test_create_model_empty_field(self):
serializer = self.model_serializer_class(data={})
self.assertEqual(serializer.is_valid(), True)
def test_create_model_null_field_save(self):
"""
Regression test for #1330.
https://github.com/tomchristie/django-rest-framework/pull/1330
"""
serializer = self.model_serializer_class(data={'title': None})
self.assertEqual(serializer.is_valid(), True)
try:
serializer.save()
except Exception:
self.fail('Exception raised on save() after validation passes')
# Test for issue #460
class SerializerPickleTests(TestCase):
"""
Test pickleability of the output of Serializers
"""
def test_pickle_simple_model_serializer_data(self):
"""
Test simple serializer
"""
pickle.dumps(PersonSerializer(Person(name="Methusela", age=969)).data)
def test_pickle_inner_serializer(self):
"""
Test pickling a serializer whose resulting .data (a SortedDictWithMetadata) will
have unpickleable meta data--in order to make sure metadata doesn't get pulled into the pickle.
See DictWithMetadata.__getstate__
"""
class InnerPersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ('name', 'age')
pickle.dumps(InnerPersonSerializer(Person(name="Noah", age=950)).data, 0)
def test_getstate_method_should_not_return_none(self):
"""
Regression test for #645.
"""
data = serializers.DictWithMetadata({1: 1})
self.assertEqual(data.__getstate__(), serializers.SortedDict({1: 1}))
def test_serializer_data_is_pickleable(self):
"""
Another regression test for #645.
"""
data = serializers.SortedDictWithMetadata({1: 1})
repr(pickle.loads(pickle.dumps(data, 0)))
# test for issue #725
class SeveralChoicesModel(models.Model):
color = models.CharField(
max_length=10,
choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')],
blank=False
)
drink = models.CharField(
max_length=10,
choices=[('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')],
blank=False,
default='beer'
)
os = models.CharField(
max_length=10,
choices=[('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')],
blank=True
)
music_genre = models.CharField(
max_length=10,
choices=[('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')],
blank=True,
default='metal'
)
class SerializerChoiceFields(TestCase):
def setUp(self):
super(SerializerChoiceFields, self).setUp()
class SeveralChoicesSerializer(serializers.ModelSerializer):
class Meta:
model = SeveralChoicesModel
fields = ('color', 'drink', 'os', 'music_genre')
self.several_choices_serializer = SeveralChoicesSerializer
def test_choices_blank_false_not_default(self):
serializer = self.several_choices_serializer()
self.assertEqual(
serializer.fields['color'].choices,
[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')]
)
def test_choices_blank_false_with_default(self):
serializer = self.several_choices_serializer()
self.assertEqual(
serializer.fields['drink'].choices,
[('beer', 'Beer'), ('wine', 'Wine'), ('cider', 'Cider')]
)
def test_choices_blank_true_not_default(self):
serializer = self.several_choices_serializer()
self.assertEqual(
serializer.fields['os'].choices,
BLANK_CHOICE_DASH + [('linux', 'Linux'), ('osx', 'OSX'), ('windows', 'Windows')]
)
def test_choices_blank_true_with_default(self):
serializer = self.several_choices_serializer()
self.assertEqual(
serializer.fields['music_genre'].choices,
BLANK_CHOICE_DASH + [('rock', 'Rock'), ('metal', 'Metal'), ('grunge', 'Grunge')]
)
# Regression tests for #675
class Ticket(models.Model):
assigned = models.ForeignKey(
Person, related_name='assigned_tickets')
reviewer = models.ForeignKey(
Person, blank=True, null=True, related_name='reviewed_tickets')
class SerializerRelatedChoicesTest(TestCase):
def setUp(self):
super(SerializerRelatedChoicesTest, self).setUp()
class RelatedChoicesSerializer(serializers.ModelSerializer):
class Meta:
model = Ticket
fields = ('assigned', 'reviewer')
self.related_fields_serializer = RelatedChoicesSerializer
def test_empty_queryset_required(self):
serializer = self.related_fields_serializer()
self.assertEqual(serializer.fields['assigned'].queryset.count(), 0)
self.assertEqual(
[x for x in serializer.fields['assigned'].widget.choices],
[]
)
def test_empty_queryset_not_required(self):
serializer = self.related_fields_serializer()
self.assertEqual(serializer.fields['reviewer'].queryset.count(), 0)
self.assertEqual(
[x for x in serializer.fields['reviewer'].widget.choices],
[('', '---------')]
)
def test_with_some_persons_required(self):
Person.objects.create(name="Lionel Messi")
Person.objects.create(name="Xavi Hernandez")
serializer = self.related_fields_serializer()
self.assertEqual(serializer.fields['assigned'].queryset.count(), 2)
self.assertEqual(
[x for x in serializer.fields['assigned'].widget.choices],
[(1, 'Person object - 1'), (2, 'Person object - 2')]
)
def test_with_some_persons_not_required(self):
Person.objects.create(name="Lionel Messi")
Person.objects.create(name="Xavi Hernandez")
serializer = self.related_fields_serializer()
self.assertEqual(serializer.fields['reviewer'].queryset.count(), 2)
self.assertEqual(
[x for x in serializer.fields['reviewer'].widget.choices],
[('', '---------'), (1, 'Person object - 1'), (2, 'Person object - 2')]
)
class DepthTest(TestCase):
def test_implicit_nesting(self):
writer = Person.objects.create(name="django", age=1)
post = BlogPost.objects.create(title="Test blog post", writer=writer)
comment = BlogPostComment.objects.create(text="Test blog post comment", blog_post=post)
class BlogPostCommentSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPostComment
depth = 2
serializer = BlogPostCommentSerializer(instance=comment)
expected = {'id': 1, 'text': 'Test blog post comment', 'blog_post': {'id': 1, 'title': 'Test blog post',
'writer': {'id': 1, 'name': 'django', 'age': 1}}}
self.assertEqual(serializer.data, expected)
def test_explicit_nesting(self):
writer = Person.objects.create(name="django", age=1)
post = BlogPost.objects.create(title="Test blog post", writer=writer)
comment = BlogPostComment.objects.create(text="Test blog post comment", blog_post=post)
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
class BlogPostSerializer(serializers.ModelSerializer):
writer = PersonSerializer()
class Meta:
model = BlogPost
class BlogPostCommentSerializer(serializers.ModelSerializer):
blog_post = BlogPostSerializer()
class Meta:
model = BlogPostComment
serializer = BlogPostCommentSerializer(instance=comment)
expected = {'id': 1, 'text': 'Test blog post comment', 'blog_post': {'id': 1, 'title': 'Test blog post',
'writer': {'id': 1, 'name': 'django', 'age': 1}}}
self.assertEqual(serializer.data, expected)
class NestedSerializerContextTests(TestCase):
def test_nested_serializer_context(self):
"""
Regression for #497
https://github.com/tomchristie/django-rest-framework/issues/497
"""
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
fields = ("description", "callable")
callable = serializers.SerializerMethodField('_callable')
def _callable(self, instance):
if 'context_item' not in self.context:
raise RuntimeError("context isn't getting passed into 2nd level nested serializer")
return "success"
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
model = Album
fields = ("photo_set", "callable")
photo_set = PhotoSerializer(source="photo_set", many=True)
callable = serializers.SerializerMethodField("_callable")
def _callable(self, instance):
if 'context_item' not in self.context:
raise RuntimeError("context isn't getting passed into 1st level nested serializer")
return "success"
class AlbumCollection(object):
albums = None
class AlbumCollectionSerializer(serializers.Serializer):
albums = AlbumSerializer(source="albums", many=True)
album1 = Album.objects.create(title="album 1")
album2 = Album.objects.create(title="album 2")
Photo.objects.create(description="Bigfoot", album=album1)
Photo.objects.create(description="Unicorn", album=album1)
Photo.objects.create(description="Yeti", album=album2)
Photo.objects.create(description="Sasquatch", album=album2)
album_collection = AlbumCollection()
album_collection.albums = [album1, album2]
# This will raise RuntimeError if context doesn't get passed correctly to the nested Serializers
AlbumCollectionSerializer(album_collection, context={'context_item': 'album context'}).data
class DeserializeListTestCase(TestCase):
def setUp(self):
self.data = {
'email': 'nobody@nowhere.com',
'content': 'This is some test content',
'created': datetime.datetime(2013, 3, 7),
}
def test_no_errors(self):
data = [self.data.copy() for x in range(0, 3)]
serializer = CommentSerializer(data=data, many=True)
self.assertTrue(serializer.is_valid())
self.assertTrue(isinstance(serializer.object, list))
self.assertTrue(
all((isinstance(item, Comment) for item in serializer.object))
)
def test_errors_return_as_list(self):
invalid_item = self.data.copy()
invalid_item['email'] = ''
data = [self.data.copy(), invalid_item, self.data.copy()]
serializer = CommentSerializer(data=data, many=True)
self.assertFalse(serializer.is_valid())
expected = [{}, {'email': ['This field is required.']}, {}]
self.assertEqual(serializer.errors, expected)
# Test for issue 747
class LazyStringModel(object):
def __init__(self, lazystring):
self.lazystring = lazystring
class LazyStringSerializer(serializers.Serializer):
lazystring = serializers.Field()
def restore_object(self, attrs, instance=None):
if instance is not None:
instance.lazystring = attrs.get('lazystring', instance.lazystring)
return instance
return LazyStringModel(**attrs)
class LazyStringsTestCase(TestCase):
def setUp(self):
self.model = LazyStringModel(lazystring=_('lazystring'))
def test_lazy_strings_are_translated(self):
serializer = LazyStringSerializer(self.model)
self.assertEqual(type(serializer.data['lazystring']),
type('lazystring'))
# Test for issue #467
class FieldLabelTest(TestCase):
def setUp(self):
self.serializer_class = BasicModelSerializer
def test_label_from_model(self):
"""
Validates that label and help_text are correctly copied from the model class.
"""
serializer = self.serializer_class()
text_field = serializer.fields['text']
self.assertEqual('Text comes here', text_field.label)
self.assertEqual('Text description.', text_field.help_text)
def test_field_ctor(self):
"""
This is check that ctor supports both label and help_text.
"""
self.assertEqual('Label', fields.Field(label='Label', help_text='Help').label)
self.assertEqual('Help', fields.CharField(label='Label', help_text='Help').help_text)
self.assertEqual('Label', relations.HyperlinkedRelatedField(view_name='fake', label='Label', help_text='Help', many=True).label)
# Test for issue #961
class ManyFieldHelpTextTest(TestCase):
def test_help_text_no_hold_down_control_msg(self):
"""
Validate that help_text doesn't contain the 'Hold down "Control" ...'
message that Django appends to choice fields.
"""
rel_field = fields.Field(help_text=ManyToManyModel._meta.get_field('rel').help_text)
self.assertEqual('Some help text.', rel_field.help_text)
class AttributeMappingOnAutogeneratedRelatedFields(TestCase):
def test_primary_key_related_field(self):
serializer = ForeignKeySourceSerializer()
self.assertEqual(serializer.fields['target'].help_text, 'Target')
self.assertEqual(serializer.fields['target'].label, 'Target')
def test_hyperlinked_related_field(self):
serializer = HyperlinkedForeignKeySourceSerializer()
self.assertEqual(serializer.fields['target'].help_text, 'Target')
self.assertEqual(serializer.fields['target'].label, 'Target')
@unittest.skipUnless(PIL is not None, 'PIL is not installed')
class AttributeMappingOnAutogeneratedFieldsTests(TestCase):
def setUp(self):
class AMOAFSerializer(serializers.ModelSerializer):
class Meta:
model = AMOAFModel
self.serializer_class = AMOAFSerializer
self.fields_attributes = {
'char_field': [
('max_length', 1024),
],
'comma_separated_integer_field': [
('max_length', 1024),
],
'decimal_field': [
('max_digits', 64),
('decimal_places', 32),
],
'email_field': [
('max_length', 1024),
],
'file_field': [
('max_length', 1024),
],
'image_field': [
('max_length', 1024),
],
'slug_field': [
('max_length', 1024),
],
'url_field': [
('max_length', 1024),
],
'nullable_char_field': [
('max_length', 1024),
('allow_none', True),
],
}
def field_test(self, field):
serializer = self.serializer_class(data={})
self.assertEqual(serializer.is_valid(), True)
for attribute in self.fields_attributes[field]:
self.assertEqual(
getattr(serializer.fields[field], attribute[0]),
attribute[1]
)
def test_char_field(self):
self.field_test('char_field')
def test_comma_separated_integer_field(self):
self.field_test('comma_separated_integer_field')
def test_decimal_field(self):
self.field_test('decimal_field')
def test_email_field(self):
self.field_test('email_field')
def test_file_field(self):
self.field_test('file_field')
def test_image_field(self):
self.field_test('image_field')
def test_slug_field(self):
self.field_test('slug_field')
def test_url_field(self):
self.field_test('url_field')
def test_nullable_char_field(self):
self.field_test('nullable_char_field')
@unittest.skipUnless(PIL is not None, 'PIL is not installed')
class DefaultValuesOnAutogeneratedFieldsTests(TestCase):
def setUp(self):
class DVOAFSerializer(serializers.ModelSerializer):
class Meta:
model = DVOAFModel
self.serializer_class = DVOAFSerializer
self.fields_attributes = {
'positive_integer_field': [
('min_value', 0),
],
'positive_small_integer_field': [
('min_value', 0),
],
'email_field': [
('max_length', 75),
],
'file_field': [
('max_length', 100),
],
'image_field': [
('max_length', 100),
],
'slug_field': [
('max_length', 50),
],
'url_field': [
('max_length', 200),
],
}
def field_test(self, field):
serializer = self.serializer_class(data={})
self.assertEqual(serializer.is_valid(), True)
for attribute in self.fields_attributes[field]:
self.assertEqual(
getattr(serializer.fields[field], attribute[0]),
attribute[1]
)
def test_positive_integer_field(self):
self.field_test('positive_integer_field')
def test_positive_small_integer_field(self):
self.field_test('positive_small_integer_field')
def test_email_field(self):
self.field_test('email_field')
def test_file_field(self):
self.field_test('file_field')
def test_image_field(self):
self.field_test('image_field')
def test_slug_field(self):
self.field_test('slug_field')
def test_url_field(self):
self.field_test('url_field')
class MetadataSerializer(serializers.Serializer):
field1 = serializers.CharField(3, required=True)
field2 = serializers.CharField(10, required=False)
class MetadataSerializerTestCase(TestCase):
def setUp(self):
self.serializer = MetadataSerializer()
def test_serializer_metadata(self):
metadata = self.serializer.metadata()
expected = {
'field1': {
'required': True,
'max_length': 3,
'type': 'string',
'read_only': False
},
'field2': {
'required': False,
'max_length': 10,
'type': 'string',
'read_only': False
}
}
self.assertEqual(expected, metadata)
# Regression test for #840
class SimpleModel(models.Model):
text = models.CharField(max_length=100)
class SimpleModelSerializer(serializers.ModelSerializer):
text = serializers.CharField()
other = serializers.CharField()
class Meta:
model = SimpleModel
def validate_other(self, attrs, source):
del attrs['other']
return attrs
class FieldValidationRemovingAttr(TestCase):
def test_removing_non_model_field_in_validation(self):
"""
Removing an attr during field valiation should ensure that it is not
passed through when restoring the object.
This allows additional non-model fields to be supported.
Regression test for #840.
"""
serializer = SimpleModelSerializer(data={'text': 'foo', 'other': 'bar'})
self.assertTrue(serializer.is_valid())
serializer.save()
self.assertEqual(serializer.object.text, 'foo')
# Regression test for #878
class SimpleTargetModel(models.Model):
text = models.CharField(max_length=100)
class SimplePKSourceModelSerializer(serializers.Serializer):
targets = serializers.PrimaryKeyRelatedField(queryset=SimpleTargetModel.objects.all(), many=True)
text = serializers.CharField()
class SimpleSlugSourceModelSerializer(serializers.Serializer):
targets = serializers.SlugRelatedField(queryset=SimpleTargetModel.objects.all(), many=True, slug_field='pk')
text = serializers.CharField()
class SerializerSupportsManyRelationships(TestCase):
def setUp(self):
SimpleTargetModel.objects.create(text='foo')
SimpleTargetModel.objects.create(text='bar')
def test_serializer_supports_pk_many_relationships(self):
"""
Regression test for #878.
Note that pk behavior has a different code path to usual cases,
for performance reasons.
"""
serializer = SimplePKSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]})
self.assertTrue(serializer.is_valid())
self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]})
def test_serializer_supports_slug_many_relationships(self):
"""
Regression test for #878.
"""
serializer = SimpleSlugSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]})
self.assertTrue(serializer.is_valid())
self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]})
class TransformMethodsSerializer(serializers.Serializer):
a = serializers.CharField()
b_renamed = serializers.CharField(source='b')
def transform_a(self, obj, value):
return value.lower()
def transform_b_renamed(self, obj, value):
if value is not None:
return 'and ' + value
class TestSerializerTransformMethods(TestCase):
def setUp(self):
self.s = TransformMethodsSerializer()
def test_transform_methods(self):
self.assertEqual(
self.s.to_native({'a': 'GREEN EGGS', 'b': 'HAM'}),
{
'a': 'green eggs',
'b_renamed': 'and HAM',
}
)
def test_missing_fields(self):
self.assertEqual(
self.s.to_native({'a': 'GREEN EGGS'}),
{
'a': 'green eggs',
'b_renamed': None,
}
)
class DefaultTrueBooleanModel(models.Model):
cat = models.BooleanField(default=True)
dog = models.BooleanField(default=False)
class SerializerDefaultTrueBoolean(TestCase):
def setUp(self):
super(SerializerDefaultTrueBoolean, self).setUp()
class DefaultTrueBooleanSerializer(serializers.ModelSerializer):
class Meta:
model = DefaultTrueBooleanModel
fields = ('cat', 'dog')
self.default_true_boolean_serializer = DefaultTrueBooleanSerializer
def test_enabled_as_false(self):
serializer = self.default_true_boolean_serializer(data={'cat': False,
'dog': False})
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], False)
self.assertEqual(serializer.data['dog'], False)
def test_enabled_as_true(self):
serializer = self.default_true_boolean_serializer(data={'cat': True,
'dog': True})
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], True)
self.assertEqual(serializer.data['dog'], True)
def test_enabled_partial(self):
serializer = self.default_true_boolean_serializer(data={'cat': False},
partial=True)
self.assertEqual(serializer.is_valid(), True)
self.assertEqual(serializer.data['cat'], False)
self.assertEqual(serializer.data['dog'], False)
class BoolenFieldTypeTest(TestCase):
'''
Ensure the various Boolean based model fields are rendered as the proper
field type
'''
def setUp(self):
'''
Setup an ActionItemSerializer for BooleanTesting
'''
data = {
'title': 'b' * 201,
}
self.serializer = ActionItemSerializer(data=data)
def test_booleanfield_type(self):
'''
Test that BooleanField is infered from models.BooleanField
'''
bfield = self.serializer.get_fields()['done']
self.assertEqual(type(bfield), fields.BooleanField)
def test_nullbooleanfield_type(self):
'''
Test that BooleanField is infered from models.NullBooleanField
https://groups.google.com/forum/#!topic/django-rest-framework/D9mXEftpuQ8
'''
bfield = self.serializer.get_fields()['started']
self.assertEqual(type(bfield), fields.BooleanField)