Commit 9ec08ce5 by Tom Christie

Merge master

parents 3cc39ffb 9d24809a
...@@ -60,7 +60,7 @@ For example, you can append `router.urls` to a list of existing views… ...@@ -60,7 +60,7 @@ For example, you can append `router.urls` to a list of existing views…
router.register(r'accounts', AccountViewSet) router.register(r'accounts', AccountViewSet)
urlpatterns = [ urlpatterns = [
url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),
] ]
urlpatterns += router.urls urlpatterns += router.urls
...@@ -68,14 +68,14 @@ For example, you can append `router.urls` to a list of existing views… ...@@ -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… Alternatively you can use Django's `include` function, like so…
urlpatterns = [ urlpatterns = [
url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),
url(r'^', include(router.urls)) url(r'^', include(router.urls))
] ]
Router URL patterns can also be namespaces. Router URL patterns can also be namespaces.
urlpatterns = [ urlpatterns = [
url(r'^forgot-password/$, ForgotPasswordFormView.as_view(), url(r'^forgot-password/$', ForgotPasswordFormView.as_view(),
url(r'^api/', include(router.urls, namespace='api')) url(r'^api/', include(router.urls, namespace='api'))
] ]
......
...@@ -198,7 +198,7 @@ Open the file `snippets/serializers.py` again, and replace the `SnippetSerialize ...@@ -198,7 +198,7 @@ Open the file `snippets/serializers.py` again, and replace the `SnippetSerialize
model = Snippet model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style') 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 >>> from snippets.serializers import SnippetSerializer
>>> serializer = SnippetSerializer() >>> serializer = SnippetSerializer()
......
...@@ -18,7 +18,7 @@ def unicode_repr(instance): ...@@ -18,7 +18,7 @@ def unicode_repr(instance):
# Get the repr of an instance, but ensure it is a unicode string # 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). # on both python 3 (already the case) and 2 (not the case).
if six.PY2: if six.PY2:
repr(instance).decode('utf-8') return repr(instance).decode('utf-8')
return repr(instance) return repr(instance)
......
...@@ -253,7 +253,7 @@ class SerializerMetaclass(type): ...@@ -253,7 +253,7 @@ class SerializerMetaclass(type):
# If this class is subclassing another Serializer, add that Serializer's # If this class is subclassing another Serializer, add that Serializer's
# fields. Note that we loop over the bases in *reverse*. This is necessary # fields. Note that we loop over the bases in *reverse*. This is necessary
# in order to maintain the correct order of fields. # in order to maintain the correct order of fields.
for base in bases[::-1]: for base in reversed(bases):
if hasattr(base, '_declared_fields'): if hasattr(base, '_declared_fields'):
fields = list(base._declared_fields.items()) + fields fields = list(base._declared_fields.items()) + fields
...@@ -899,7 +899,15 @@ class ModelSerializer(Serializer): ...@@ -899,7 +899,15 @@ class ModelSerializer(Serializer):
if fields is not None: if fields is not None:
# Ensure that all declared fields have also been included in the # Ensure that all declared fields have also been included in the
# `Meta.fields` option. # `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, ( assert field_name in fields, (
"The field '{field_name}' was declared on serializer " "The field '{field_name}' was declared on serializer "
"{serializer_class}, but has not been included in the " "{serializer_class}, but has not been included in the "
......
...@@ -105,3 +105,6 @@ class BindingDict(collections.MutableMapping): ...@@ -105,3 +105,6 @@ class BindingDict(collections.MutableMapping):
def __len__(self): def __len__(self):
return len(self.fields) return len(self.fields)
def __repr__(self):
return dict.__repr__(self.fields)
...@@ -5,11 +5,14 @@ shortcuts for automatically creating serializers based on a given model class. ...@@ -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 These tests deal with ensuring that we correctly map the model fields onto
an appropriate set of serializer fields for each case. an appropriate set of serializer fields for each case.
""" """
from __future__ import unicode_literals
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.validators import MaxValueValidator, MinValueValidator, MinLengthValidator from django.core.validators import MaxValueValidator, MinValueValidator, MinLengthValidator
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.utils import six
from rest_framework import serializers from rest_framework import serializers
from rest_framework.compat import unicode_repr
def dedent(blocktext): def dedent(blocktext):
...@@ -124,7 +127,7 @@ class TestRegularFieldMappings(TestCase): ...@@ -124,7 +127,7 @@ class TestRegularFieldMappings(TestCase):
url_field = URLField(max_length=100) url_field = URLField(max_length=100)
custom_field = ModelField(model_field=<tests.test_model_serializer.CustomField: custom_field>) 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): def test_field_options(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -142,7 +145,14 @@ class TestRegularFieldMappings(TestCase): ...@@ -142,7 +145,14 @@ class TestRegularFieldMappings(TestCase):
descriptive_field = IntegerField(help_text='Some help text', label='A label') descriptive_field = IntegerField(help_text='Some help text', label='A label')
choices_field = ChoiceField(choices=[('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')]) 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): def test_method_field(self):
""" """
...@@ -229,6 +239,26 @@ class TestRegularFieldMappings(TestCase): ...@@ -229,6 +239,26 @@ class TestRegularFieldMappings(TestCase):
) )
assert str(excinfo.exception) == expected 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. # Tests for relational field mappings.
# ------------------------------------ # ------------------------------------
...@@ -276,7 +306,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -276,7 +306,7 @@ class TestRelationalFieldMappings(TestCase):
many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all()) many_to_many = PrimaryKeyRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all())
through = PrimaryKeyRelatedField(many=True, read_only=True) through = PrimaryKeyRelatedField(many=True, read_only=True)
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(unicode_repr(TestSerializer()), expected)
def test_nested_relations(self): def test_nested_relations(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -300,7 +330,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -300,7 +330,7 @@ class TestRelationalFieldMappings(TestCase):
id = IntegerField(label='ID', read_only=True) id = IntegerField(label='ID', read_only=True)
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(unicode_repr(TestSerializer()), expected)
def test_hyperlinked_relations(self): def test_hyperlinked_relations(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
...@@ -315,7 +345,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -315,7 +345,7 @@ class TestRelationalFieldMappings(TestCase):
many_to_many = HyperlinkedRelatedField(many=True, queryset=ManyToManyTargetModel.objects.all(), view_name='manytomanytargetmodel-detail') 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') 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): def test_nested_hyperlinked_relations(self):
class TestSerializer(serializers.HyperlinkedModelSerializer): class TestSerializer(serializers.HyperlinkedModelSerializer):
...@@ -339,7 +369,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -339,7 +369,7 @@ class TestRelationalFieldMappings(TestCase):
url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail') url = HyperlinkedIdentityField(view_name='throughtargetmodel-detail')
name = CharField(max_length=100) name = CharField(max_length=100)
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(unicode_repr(TestSerializer()), expected)
def test_pk_reverse_foreign_key(self): def test_pk_reverse_foreign_key(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -353,7 +383,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -353,7 +383,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_foreign_key = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all()) 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): def test_pk_reverse_one_to_one(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -367,7 +397,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -367,7 +397,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_one_to_one = PrimaryKeyRelatedField(queryset=RelationalModel.objects.all()) 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): def test_pk_reverse_many_to_many(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -381,7 +411,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -381,7 +411,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_many_to_many = PrimaryKeyRelatedField(many=True, queryset=RelationalModel.objects.all()) 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): def test_pk_reverse_through(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
...@@ -395,7 +425,7 @@ class TestRelationalFieldMappings(TestCase): ...@@ -395,7 +425,7 @@ class TestRelationalFieldMappings(TestCase):
name = CharField(max_length=100) name = CharField(max_length=100)
reverse_through = PrimaryKeyRelatedField(many=True, read_only=True) reverse_through = PrimaryKeyRelatedField(many=True, read_only=True)
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(unicode_repr(TestSerializer()), expected)
class TestIntegration(TestCase): class TestIntegration(TestCase):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment