Ensure model field validation is performed for ModelSerializers with a custom restore_object method. Fixes #623.

This commit is contained in:
Tom Christie 2013-01-28 12:56:42 +00:00
parent 94c4a54bf8
commit a3a06d11cc
2 changed files with 51 additions and 6 deletions

View File

@ -513,6 +513,22 @@ class ModelSerializer(Serializer):
exclusions.remove(field_name) exclusions.remove(field_name)
return exclusions return exclusions
def full_clean(self, instance):
"""
Perform Django's full_clean, and populate the `errors` dictionary
if any validation errors occur.
Note that we don't perform this inside the `.restore_object()` method,
so that subclasses can override `.restore_object()`, and still get
the full_clean validation checking.
"""
try:
instance.full_clean(exclude=self.get_validation_exclusions())
except ValidationError, err:
self._errors = err.message_dict
return None
return instance
def restore_object(self, attrs, instance=None): def restore_object(self, attrs, instance=None):
""" """
Restore the model instance. Restore the model instance.
@ -544,14 +560,16 @@ class ModelSerializer(Serializer):
else: else:
instance = self.opts.model(**attrs) instance = self.opts.model(**attrs)
try:
instance.full_clean(exclude=self.get_validation_exclusions())
except ValidationError, err:
self._errors = err.message_dict
return None
return instance return instance
def from_native(self, data, files):
"""
Override the default method to also include model field validation.
"""
instance = super(ModelSerializer, self).from_native(data, files)
if instance:
return self.full_clean(instance)
def save(self): def save(self):
""" """
Save the deserialized object and return it. Save the deserialized object and return it.

View File

@ -54,6 +54,19 @@ class ActionItemSerializer(serializers.ModelSerializer):
model = ActionItem model = ActionItem
class ActionItemSerializerCustomRestore(serializers.ModelSerializer):
class Meta:
model = ActionItem
def restore_object(self, data, instance=None):
if instance is None:
return ActionItem(**data)
for key, val in data.items():
setattr(instance, key, val)
return instance
class PersonSerializer(serializers.ModelSerializer): class PersonSerializer(serializers.ModelSerializer):
info = serializers.Field(source='info') info = serializers.Field(source='info')
@ -273,6 +286,20 @@ class ValidationTests(TestCase):
self.assertEquals(serializer.is_valid(), False) self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']}) self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']})
def test_modelserializer_max_length_exceeded_with_custom_restore(self):
"""
When overriding ModelSerializer.restore_object, validation tests should still apply.
Regression test for #623.
https://github.com/tomchristie/django-rest-framework/pull/623
"""
data = {
'title': 'x' * 201,
}
serializer = ActionItemSerializerCustomRestore(data=data)
self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']})
def test_default_modelfield_max_length_exceeded(self): def test_default_modelfield_max_length_exceeded(self):
data = { data = {
'title': 'Testing "info" field...', 'title': 'Testing "info" field...',