diff options
| author | Tom Christie | 2014-09-18 12:17:21 +0100 | 
|---|---|---|
| committer | Tom Christie | 2014-09-18 12:17:21 +0100 | 
| commit | 87734be5f41de921ac32ad1f6664db243aab6d07 (patch) | |
| tree | 7c6b8d18dc0d505525154c69d112925a95174b5d | |
| parent | 5b7e4af0d657a575cb15eea85a63a7100c636085 (diff) | |
| download | django-rest-framework-87734be5f41de921ac32ad1f6664db243aab6d07.tar.bz2 | |
Configuration correctness tests on ModelSerializer
| -rw-r--r-- | rest_framework/serializers.py | 30 | ||||
| -rw-r--r-- | tests/test_model_serializer.py (renamed from tests/test_model_field_mappings.py) | 85 | 
2 files changed, 112 insertions, 3 deletions
| diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 99dcc349..9f3e53fd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -10,7 +10,7 @@ python primitives.  2. The process of marshalling between python primitives and request and  response content is handled by parsers and renderers.  """ -from django.core.exceptions import ValidationError +from django.core.exceptions import ImproperlyConfigured, ValidationError  from django.db import models  from django.utils import six  from django.utils.datastructures import SortedDict @@ -358,6 +358,7 @@ class ModelSerializer(Serializer):          model = getattr(self.Meta, 'model')          fields = getattr(self.Meta, 'fields', None)          depth = getattr(self.Meta, 'depth', 0) +        extra_kwargs = getattr(self.Meta, 'extra_kwargs', {})          # Retrieve metadata about fields & relationships on the model class.          info = model_meta.get_field_info(model) @@ -405,9 +406,32 @@ class ModelSerializer(Serializer):                      if not issubclass(field_cls, HyperlinkedRelatedField):                          kwargs.pop('view_name', None) -            else: -                assert False, 'Field name `%s` is not valid.' % field_name +            elif hasattr(model, field_name): +                # Create a read only field for model methods and properties. +                field_cls = ReadOnlyField +                kwargs = {} +            else: +                raise ImproperlyConfigured( +                    'Field name `%s` is not valid for model `%s`.' % +                    (field_name, model.__class__.__name__) +                ) + +            # Check that any fields declared on the class are +            # also explicity included in `Meta.fields`. +            missing_fields = set(declared_fields.keys()) - set(fields) +            if missing_fields: +                missing_field = list(missing_fields)[0] +                raise ImproperlyConfigured( +                    'Field `%s` has been declared on serializer `%s`, but ' +                    'is missing from `Meta.fields`.' % +                    (missing_field, self.__class__.__name__) +                ) + +            # Populate any kwargs defined in `Meta.extra_kwargs` +            kwargs.update(extra_kwargs.get(field_name, {})) + +            # Create the serializer field.              ret[field_name] = field_cls(**kwargs)          return ret diff --git a/tests/test_model_field_mappings.py b/tests/test_model_serializer.py index 6daa574e..63e36452 100644 --- a/tests/test_model_field_mappings.py +++ b/tests/test_model_serializer.py @@ -5,6 +5,7 @@ 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 django.core.exceptions import ImproperlyConfigured  from django.db import models  from django.test import TestCase  from rest_framework import serializers @@ -37,6 +38,9 @@ class RegularFieldsModel(models.Model):      time_field = models.TimeField()      url_field = models.URLField(max_length=100) +    def method(self): +        return 'method' +  class TestRegularFieldMappings(TestCase):      def test_regular_fields(self): @@ -69,6 +73,87 @@ class TestRegularFieldMappings(TestCase):          self.assertEqual(repr(TestSerializer()), expected) +    def test_method_field(self): +        """ +        Properties and methods on the model should be allowed as `Meta.fields` +        values, and should map to `ReadOnlyField`. +        """ +        class TestSerializer(serializers.ModelSerializer): +            class Meta: +                model = RegularFieldsModel +                fields = ('auto_field', 'method') + +        expected = dedent(""" +            TestSerializer(): +                auto_field = IntegerField(read_only=True) +                method = ReadOnlyField() +        """) +        self.assertEqual(repr(TestSerializer()), expected) + +    def test_pk_fields(self): +        """ +        Both `pk` and the actual primary key name are valid in `Meta.fields`. +        """ +        class TestSerializer(serializers.ModelSerializer): +            class Meta: +                model = RegularFieldsModel +                fields = ('pk', 'auto_field') + +        expected = dedent(""" +            TestSerializer(): +                pk = IntegerField(label='Auto field', read_only=True) +                auto_field = IntegerField(read_only=True) +        """) +        self.assertEqual(repr(TestSerializer()), expected) + +    def test_extra_field_kwargs(self): +        """ +        Ensure `extra_kwargs` are passed to generated fields. +        """ +        class TestSerializer(serializers.ModelSerializer): +            class Meta: +                model = RegularFieldsModel +                fields = ('pk', 'char_field') +                extra_kwargs = {'char_field': {'default': 'extra'}} + +        expected = dedent(""" +            TestSerializer(): +                pk = IntegerField(label='Auto field', read_only=True) +                char_field = CharField(default='extra', max_length=100) +        """) +        self.assertEqual(repr(TestSerializer()), expected) + +    def test_invalid_field(self): +        """ +        Field names that do not map to a model field or relationship should +        raise a configuration errror. +        """ +        class TestSerializer(serializers.ModelSerializer): +            class Meta: +                model = RegularFieldsModel +                fields = ('auto_field', 'invalid') + +        with self.assertRaises(ImproperlyConfigured) as excinfo: +            TestSerializer() +        expected = 'Field name `invalid` is not valid for model `ModelBase`.' +        assert str(excinfo.exception) == expected + +    def test_missing_field(self): +        class TestSerializer(serializers.ModelSerializer): +            missing = serializers.ReadOnlyField() + +            class Meta: +                model = RegularFieldsModel +                fields = ('auto_field',) + +        with self.assertRaises(ImproperlyConfigured) as excinfo: +            TestSerializer() +        expected = ( +            'Field `missing` has been declared on serializer ' +            '`TestSerializer`, but is missing from `Meta.fields`.' +        ) +        assert str(excinfo.exception) == expected +  # Testing relational field mappings | 
