diff options
| author | Tom Christie | 2012-12-11 21:07:25 +0000 | 
|---|---|---|
| committer | Tom Christie | 2012-12-11 21:07:25 +0000 | 
| commit | 405822330958c5432dde56b07a61b223c03ca4c7 (patch) | |
| tree | 5f6305d47e82c37f597d4ae533b0a65546ebd7f0 | |
| parent | 17b77fc446df29e7708c210eade8369c7babc466 (diff) | |
| download | django-rest-framework-405822330958c5432dde56b07a61b223c03ca4c7.tar.bz2 | |
Fix broken nested fields
| -rw-r--r-- | rest_framework/compat.py | 10 | ||||
| -rw-r--r-- | rest_framework/fields.py | 7 | ||||
| -rw-r--r-- | rest_framework/serializers.py | 38 | ||||
| -rw-r--r-- | rest_framework/tests/serializer.py | 31 | 
4 files changed, 55 insertions, 31 deletions
| diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 09b76368..d4901437 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -19,6 +19,16 @@ except ImportError:      import StringIO +# Try to import PIL in either of the two ways it can end up installed. +try: +    from PIL import Image +except ImportError: +    try: +        import Image +    except ImportError: +        Image = None + +  def get_concrete_model(model_cls):      try:          return model_cls._meta.concrete_model diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 82745973..75ce1b9f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1056,11 +1056,8 @@ class ImageField(FileField):          if f is None:              return None -        # Try to import PIL in either of the two ways it can end up installed. -        try: -            from PIL import Image -        except ImportError: -            import Image +        from compat import Image +        assert Image is not None, 'PIL must be installed for ImageField support'          # We need to get a file object for PIL. We might have a path or we might          # have to read the data into memory. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index c3f260c7..ebeb43e8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -237,7 +237,8 @@ class BaseSerializer(Field):              except ValidationError as err:                  self._errors[field_name] = self._errors.get(field_name, []) + list(err.messages) -        # We don't run .validate() because field-validation failed and thus `attrs` may not be complete. +        # If there are already errors, we don't run .validate() because +        # field-validation failed and thus `attrs` may not be complete.          # which in turn can cause inconsistent validation errors.          if not self._errors:              try: @@ -299,17 +300,14 @@ class BaseSerializer(Field):          Override default so that we can apply ModelSerializer as a nested          field to relationships.          """ -          if self.source: -            value = obj              for component in self.source.split('.'): -                value = getattr(value, component) -                if is_simple_callable(value): -                    value = value() -            obj = value +                obj = getattr(obj, component) +                if is_simple_callable(obj): +                    obj = obj()          else: -            value = getattr(obj, field_name) -            if is_simple_callable(value): +            obj = getattr(obj, field_name) +            if is_simple_callable(obj):                  obj = value()          # If the object has an "all" method, assume it's a relationship @@ -486,15 +484,10 @@ class ModelSerializer(Serializer):          except KeyError:              return ModelField(model_field=model_field, **kwargs) -    def validate(self, attrs): -        copied_attrs = copy.deepcopy(attrs) -        restored_object = self.restore_object(copied_attrs, instance=getattr(self, 'object', None)) -        self.perform_model_validation(restored_object) -        return attrs - -    def perform_model_validation(self, restored_object): -        # Call Django's full_clean() which in turn calls: Model.clean_fields(), Model.clean(), Model.validat_unique() -        restored_object.full_clean(exclude=list(self.get_excluded_fieldnames())) +    # def validate(self, attrs): +    #     restored_object = self.restore_object(attrs, instance=getattr(self, 'object', None)) +    #     restored_object.full_clean(exclude=list(self.get_excluded_fieldnames())) +    #     return attrs      def restore_object(self, attrs, instance=None):          """ @@ -517,7 +510,14 @@ class ModelSerializer(Serializer):          for field in self.opts.model._meta.many_to_many:              if field.name in attrs:                  self.m2m_data[field.name] = attrs.pop(field.name) -        return self.opts.model(**attrs) + +        instance = self.opts.model(**attrs) +        try: +            instance.full_clean(exclude=list(self.get_excluded_fieldnames())) +        except ValidationError, err: +            self._errors = err.message_dict +            return None +        return instance      def save(self, save_m2m=True):          """ diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py index f80762f0..50a5f5a4 100644 --- a/rest_framework/tests/serializer.py +++ b/rest_framework/tests/serializer.py @@ -1,4 +1,5 @@ -import datetime, pickle +import datetime +import pickle  from django.test import TestCase  from rest_framework import serializers  from rest_framework.tests.models import (Album, ActionItem, Anchor, BasicModel, @@ -727,23 +728,39 @@ class SerializerPickleTests(TestCase):                  fields = ('name', 'age')          pickle.dumps(InnerPersonSerializer(Person(name="Noah", age=950)).data) +  class DepthTest(TestCase): -    def test_depth(self): -        user = Person.objects.create(name="django",age=1) -        post = BlogPost.objects.create(title="Test blog post", writer=user) +    def test_implicit_nesting(self): +        writer = Person.objects.create(name="django", age=1) +        post = BlogPost.objects.create(title="Test blog post", writer=writer) + +        class BlogPostSerializer(serializers.ModelSerializer): +            class Meta: +                model = BlogPost +                depth = 1 + +        serializer = BlogPostSerializer(instance=post) +        expected = {'id': 1, 'title': u'Test blog post', +                    'writer': {'id': 1, 'name': u'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)          class PersonSerializer(serializers.ModelSerializer):              class Meta:                  model = Person -                fields = ("name", "age")          class BlogPostSerializer(serializers.ModelSerializer): +            writer = PersonSerializer() +              class Meta:                  model = BlogPost -                depth = 1          serializer = BlogPostSerializer(instance=post)          expected = {'id': 1, 'title': u'Test blog post', -                    'writer': {'id': 1, 'name': u'django', 'age':1}} +                    'writer': {'id': 1, 'name': u'django', 'age': 1}}          self.assertEqual(serializer.data, expected) | 
