diff options
| -rw-r--r-- | docs/api-guide/routers.md | 6 | ||||
| -rw-r--r-- | docs/tutorial/1-serialization.md | 2 | ||||
| -rw-r--r-- | rest_framework/compat.py | 2 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 12 | ||||
| -rw-r--r-- | rest_framework/utils/serializer_helpers.py | 3 | ||||
| -rw-r--r-- | tests/test_model_serializer.py | 50 | 
6 files changed, 58 insertions, 17 deletions
| diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md index 3a8a8f6c..9c9bfb50 100644 --- a/docs/api-guide/routers.md +++ b/docs/api-guide/routers.md @@ -60,7 +60,7 @@ For example, you can append `router.urls` to a list of existing views…      router.register(r'accounts', AccountViewSet)      urlpatterns = [ -        url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), +        url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),      ]      urlpatterns += router.urls @@ -68,14 +68,14 @@ For example, you can append `router.urls` to a list of existing views…  Alternatively you can use Django's `include` function, like so…      urlpatterns = [ -        url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), +        url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),          url(r'^', include(router.urls))      ]  Router URL patterns can also be namespaces.      urlpatterns = [ -        url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), +        url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),          url(r'^api/', include(router.urls, namespace='api'))      ] diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 41ff4d07..80e869ea 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -198,7 +198,7 @@ Open the file `snippets/serializers.py` again, and replace the `SnippetSerialize              model = Snippet              fields = ('id', 'title', 'code', 'linenos', 'language', 'style') -One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing it's representation. Open the Django shell with `python manage.py shell`, then try the following: +One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with `python manage.py shell`, then try the following:      >>> from snippets.serializers import SnippetSerializer      >>> serializer = SnippetSerializer() diff --git a/rest_framework/compat.py b/rest_framework/compat.py index bd3802ad..a6620057 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -18,7 +18,7 @@ def unicode_repr(instance):      # Get the repr of an instance, but ensure it is a unicode string      # on both python 3 (already the case) and 2 (not the case).      if six.PY2: -        repr(instance).decode('utf-8') +        return repr(instance).decode('utf-8')      return repr(instance) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 77d3f202..b91ecebc 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -253,7 +253,7 @@ class SerializerMetaclass(type):          # If this class is subclassing another Serializer, add that Serializer's          # fields.  Note that we loop over the bases in *reverse*. This is necessary          # in order to maintain the correct order of fields. -        for base in bases[::-1]: +        for base in reversed(bases):              if hasattr(base, '_declared_fields'):                  fields = list(base._declared_fields.items()) + fields @@ -899,7 +899,15 @@ class ModelSerializer(Serializer):          if fields is not None:              # Ensure that all declared fields have also been included in the              # `Meta.fields` option. -            for field_name in declared_fields: + +            # Do not require any fields that are declared a parent class, +            # in order to allow serializer subclasses to only include +            # a subset of fields. +            required_field_names = set(declared_fields) +            for cls in self.__class__.__bases__: +                required_field_names -= set(getattr(cls, '_declared_fields', [])) + +            for field_name in required_field_names:                  assert field_name in fields, (                      "The field '{field_name}' was declared on serializer "                      "{serializer_class}, but has not been included in the " diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py index f9960603..ab057862 100644 --- a/rest_framework/utils/serializer_helpers.py +++ b/rest_framework/utils/serializer_helpers.py @@ -105,3 +105,6 @@ class BindingDict(collections.MutableMapping):      def __len__(self):          return len(self.fields) + +    def __repr__(self): +        return dict.__repr__(self.fields) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index db0f2e6f..bce2008a 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -5,11 +5,14 @@ shortcuts for automatically creating serializers based on a given model class.  These tests deal with ensuring that we correctly map the model fields onto  an appropriate set of serializer fields for each case.  """ +from __future__ import unicode_literals  from django.core.exceptions import ImproperlyConfigured  from django.core.validators import MaxValueValidator, MinValueValidator, MinLengthValidator  from django.db import models  from django.test import TestCase +from django.utils import six  from rest_framework import serializers +from rest_framework.compat import unicode_repr  def dedent(blocktext): @@ -124,7 +127,7 @@ class TestRegularFieldMappings(TestCase):                  url_field = URLField(max_length=100)                  custom_field = ModelField(model_field=<tests.test_model_serializer.CustomField: custom_field>)          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_field_options(self):          class TestSerializer(serializers.ModelSerializer): @@ -142,7 +145,14 @@ class TestRegularFieldMappings(TestCase):                  descriptive_field = IntegerField(help_text='Some help text', label='A label')                  choices_field = ChoiceField(choices=[('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')])          """) -        self.assertEqual(repr(TestSerializer()), expected) +        if six.PY2: +            # This particular case is too awkward to resolve fully across +            # both py2 and py3. +            expected = expected.replace( +                "('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')", +                "(u'red', u'Red'), (u'blue', u'Blue'), (u'green', u'Green')" +            ) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_method_field(self):          """ @@ -229,6 +239,26 @@ class TestRegularFieldMappings(TestCase):          )          assert str(excinfo.exception) == expected +    def test_missing_superclass_field(self): +        """ +        Fields that have been declared on a parent of the serializer class may +        be excluded from the `Meta.fields` option. +        """ +        class TestSerializer(serializers.ModelSerializer): +            missing = serializers.ReadOnlyField() + +            class Meta: +                model = RegularFieldsModel + +        class ChildSerializer(TestSerializer): +            missing = serializers.ReadOnlyField() + +            class Meta: +                model = RegularFieldsModel +                fields = ('auto_field',) + +        ChildSerializer().fields +  # Tests for relational field mappings.  # ------------------------------------ @@ -276,7 +306,7 @@ class TestRelationalFieldMappings(TestCase):                  many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all())                  through = PrimaryKeyRelatedField(many=True, read_only=True)          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_nested_relations(self):          class TestSerializer(serializers.ModelSerializer): @@ -300,7 +330,7 @@ class TestRelationalFieldMappings(TestCase):                      id = IntegerField(label='ID', read_only=True)                      name = CharField(max_length=100)          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_hyperlinked_relations(self):          class TestSerializer(serializers.HyperlinkedModelSerializer): @@ -315,7 +345,7 @@ class TestRelationalFieldMappings(TestCase):                  many_to_many = HyperlinkedRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail')                  through = HyperlinkedRelatedField(many=True, read_only=True, view_name='throughtargetmodel-detail')          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_nested_hyperlinked_relations(self):          class TestSerializer(serializers.HyperlinkedModelSerializer): @@ -339,7 +369,7 @@ class TestRelationalFieldMappings(TestCase):                      url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')                      name = CharField(max_length=100)          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_pk_reverse_foreign_key(self):          class TestSerializer(serializers.ModelSerializer): @@ -353,7 +383,7 @@ class TestRelationalFieldMappings(TestCase):                  name = CharField(max_length=100)                  reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_pk_reverse_one_to_one(self):          class TestSerializer(serializers.ModelSerializer): @@ -367,7 +397,7 @@ class TestRelationalFieldMappings(TestCase):                  name = CharField(max_length=100)                  reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all())          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_pk_reverse_many_to_many(self):          class TestSerializer(serializers.ModelSerializer): @@ -381,7 +411,7 @@ class TestRelationalFieldMappings(TestCase):                  name = CharField(max_length=100)                  reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all())          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)      def test_pk_reverse_through(self):          class TestSerializer(serializers.ModelSerializer): @@ -395,7 +425,7 @@ class TestRelationalFieldMappings(TestCase):                  name = CharField(max_length=100)                  reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)          """) -        self.assertEqual(repr(TestSerializer()), expected) +        self.assertEqual(unicode_repr(TestSerializer()), expected)  class TestIntegration(TestCase): | 
