aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework/serializers.py
diff options
context:
space:
mode:
authorTom Christie2012-12-07 12:34:56 -0800
committerTom Christie2012-12-07 12:34:56 -0800
commita5178e9a363d00f3eef8d86da2d0ec687518f288 (patch)
tree1a71714f9d14f7c6c8878ffba7e85f416d2f21b1 /rest_framework/serializers.py
parenta463ddbb37edb725626b5b819266f3962bafc160 (diff)
parent919aff329ee1bd214831095e4d96af71795ed572 (diff)
downloaddjango-rest-framework-a5178e9a363d00f3eef8d86da2d0ec687518f288.tar.bz2
Merge pull request #451 from markotibold/#431
Call model's .full_clean() method, eg. to validate uniqueness
Diffstat (limited to 'rest_framework/serializers.py')
-rw-r--r--rest_framework/serializers.py40
1 files changed, 32 insertions, 8 deletions
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 67eafdf0..5edd46f5 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -127,6 +127,17 @@ class BaseSerializer(Field):
"""
return {}
+ def get_excluded_fieldnames(self):
+ """
+ Returns the fieldnames that should not be validated.
+ """
+ excluded_fields = list(self.opts.exclude)
+ for field in self.fields.keys() + self.get_default_fields().keys():
+ if self.opts.fields:
+ if field not in self.opts.fields + self.opts.exclude:
+ excluded_fields.append(field)
+ return excluded_fields
+
def get_fields(self):
"""
Returns the complete set of fields for the object as a dict.
@@ -226,10 +237,17 @@ class BaseSerializer(Field):
except ValidationError as err:
self._errors[field_name] = self._errors.get(field_name, []) + list(err.messages)
- try:
- attrs = self.validate(attrs)
- except ValidationError as err:
- self._errors['non_field_errors'] = err.messages
+ # 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:
+ attrs = self.validate(attrs)
+ except ValidationError as err:
+ if hasattr(err, 'message_dict'):
+ for field_name, error_messages in err.message_dict.items():
+ self._errors[field_name] = self._errors.get(field_name, []) + list(error_messages)
+ elif hasattr(err, 'messages'):
+ self._errors['non_field_errors'] = err.messages
return attrs
@@ -441,10 +459,6 @@ class ModelSerializer(Serializer):
kwargs['choices'] = model_field.flatchoices
return ChoiceField(**kwargs)
- max_length = getattr(model_field, 'max_length', None)
- if max_length:
- kwargs['max_length'] = max_length
-
field_mapping = {
models.FloatField: FloatField,
models.IntegerField: IntegerField,
@@ -468,6 +482,16 @@ 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 restore_object(self, attrs, instance=None):
"""
Restore the model instance.