diff options
| author | Andreas Pelme | 2014-03-02 12:40:30 +0100 | 
|---|---|---|
| committer | Andreas Pelme | 2014-03-02 12:40:30 +0100 | 
| commit | 971578ca345c3d3bae7fd93b87c41d43483b6f05 (patch) | |
| tree | 72a6d3d0ced5750a8ba01bedc3a6f8a936e05e2f /tests/test_serializer.py | |
| parent | 62786a7ad64918022f11f1b95ce84adb8d798830 (diff) | |
| download | django-rest-framework-971578ca345c3d3bae7fd93b87c41d43483b6f05.tar.bz2 | |
Support for running the test suite with py.test
 * Get rid of runtests.py
 * Moved test code  from rest_framework/tests and rest_framework/runtests to tests
 * Invoke py.test from setup.py
 * Invoke py.test from Travis
 * Invoke py.test from tox
 * Changed setUpClass to be just plain setUp in test_permissions.py
 * Updated contribution guideline to show how to invoke py.test
Diffstat (limited to 'tests/test_serializer.py')
| -rw-r--r-- | tests/test_serializer.py | 1857 | 
1 files changed, 1857 insertions, 0 deletions
| diff --git a/tests/test_serializer.py b/tests/test_serializer.py new file mode 100644 index 00000000..18484afe --- /dev/null +++ b/tests/test_serializer.py @@ -0,0 +1,1857 @@ +# -*- 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.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) +from tests.models import BasicModelSerializer +import datetime +import pickle + + +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']  # lists are also valid options + + +class PositiveIntegerAsChoiceSerializer(serializers.ModelSerializer): +    class Meta: +        model = HasPositiveIntegerAsChoice +        fields = ['some_integer'] + + +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. +        """ +        self.assertRaises(AssertionError, PersonSerializerInvalidReadOnly, []) + +    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_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'}) +        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.']}) + +    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 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) + +    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) + + +#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 not 'context_item' 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") +            callable = serializers.SerializerMethodField("_callable") + +            def _callable(self, instance): +                if not 'context_item' 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") + +        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 AttributeMappingOnAutogeneratedFieldsTests(TestCase): + +    def setUp(self): +        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(max_length=1024, blank=True) +            image_field = models.ImageField(max_length=1024, blank=True) +            slug_field = models.SlugField(max_length=1024, blank=True) +            url_field = models.URLField(max_length=1024, blank=True) + +        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), +            ], +        } + +    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') + + +class DefaultValuesOnAutogeneratedFieldsTests(TestCase): + +    def setUp(self): +        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(blank=True) +            image_field = models.ImageField(blank=True) +            slug_field = models.SlugField(blank=True) +            url_field = models.URLField(blank=True) + +        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) | 
