aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rest_framework/compat.py10
-rw-r--r--rest_framework/fields.py7
-rw-r--r--rest_framework/serializers.py38
-rw-r--r--rest_framework/tests/serializer.py31
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)