aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework/validators.py
diff options
context:
space:
mode:
Diffstat (limited to 'djangorestframework/validators.py')
-rw-r--r--djangorestframework/validators.py87
1 files changed, 63 insertions, 24 deletions
diff --git a/djangorestframework/validators.py b/djangorestframework/validators.py
index aefad4b3..3d0a7794 100644
--- a/djangorestframework/validators.py
+++ b/djangorestframework/validators.py
@@ -22,6 +22,7 @@ class FormValidatorMixin(ValidatorMixin):
"""The form class that should be used for validation, or None to turn off form validation."""
form = None
+ bound_form_instance = None
def validate(self, content):
"""Given some content as input return some cleaned, validated content.
@@ -34,29 +35,39 @@ class FormValidatorMixin(ValidatorMixin):
If the 'field-errors' key exists it is a dict of {field name as string: list of errors as strings}."""
return self._validate(content)
- def _validate(self, content, extra_fields=()):
+ def _validate(self, content, allowed_extra_fields=()):
"""Wrapped by validate to hide the extra_fields option that the ModelValidatorMixin uses.
extra_fields is a list of fields which are not defined by the form, but which we still
expect to see on the input."""
- if self.form is None:
+ bound_form = self.get_bound_form(content)
+
+ if bound_form is None:
return content
- bound_form = self.get_bound_form(content)
+ self.bound_form_instance = bound_form
- # In addition to regular validation we also ensure no additional fields are being passed in...
- unknown_fields = set(content.keys()) - set(self.form().fields.keys()) - set(extra_fields)
+ seen_fields_set = set(content.keys())
+ form_fields_set = set(bound_form.fields.keys())
+ allowed_extra_fields_set = set(allowed_extra_fields)
- # And that any extra fields we have specified are all present.
- missing_extra_fields = set(extra_fields) - set(content.keys())
+ # In addition to regular validation we also ensure no additional fields are being passed in...
+ unknown_fields = seen_fields_set - (form_fields_set | allowed_extra_fields_set)
# Check using both regular validation, and our stricter no additional fields rule
- if bound_form.is_valid() and not unknown_fields and not missing_extra_fields:
- return bound_form.cleaned_data
+ if bound_form.is_valid() and not unknown_fields:
+ # Validation succeeded...
+ cleaned_data = bound_form.cleaned_data
+
+ # Add in any extra fields to the cleaned content...
+ for key in (allowed_extra_fields_set & seen_fields_set) - set(cleaned_data.keys()):
+ cleaned_data[key] = content[key]
+
+ return cleaned_data
# Validation failed...
detail = {}
- if not bound_form.errors and not unknown_fields and not missing_extra_fields:
+ if not bound_form.errors and not unknown_fields:
detail = {u'errors': [u'No content was supplied.']}
else:
@@ -70,10 +81,6 @@ class FormValidatorMixin(ValidatorMixin):
# Add any unknown field errors
for key in unknown_fields:
field_errors[key] = [u'This field does not exist.']
-
- # Add any missing fields that we required by the extra fields argument
- for key in missing_extra_fields:
- field_errors[key] = [u'This field is required.']
if field_errors:
detail[u'field-errors'] = field_errors
@@ -105,8 +112,14 @@ class ModelFormValidatorMixin(FormValidatorMixin):
model = None
"""The list of fields we expect to receive as input. Fields in this list will may be received with
- raising non-existent field errors, even if they do not exist as fields on the ModelForm."""
+ raising non-existent field errors, even if they do not exist as fields on the ModelForm.
+
+ Setting the fields class attribute causes the exclude_fields class attribute to be disregarded."""
fields = None
+
+ """The list of fields to exclude from the Model. This is only used if the fields class attribute is not set."""
+ exclude_fields = ('id', 'pk')
+
# TODO: test the different validation here to allow for get get_absolute_url to be supplied on input and not bork out
# TODO: be really strict on fields - check they match in the handler methods. (this isn't a validator thing tho.)
@@ -122,8 +135,7 @@ class ModelFormValidatorMixin(FormValidatorMixin):
On failure the ResponseException content is a dict which may contain 'errors' and 'field-errors' keys.
If the 'errors' key exists it is a list of strings of non-field errors.
If the 'field-errors' key exists it is a dict of {field name as string: list of errors as strings}."""
- extra_fields = set(as_tuple(self.fields)) - set(self.get_bound_form().fields)
- return self._validate(content, extra_fields)
+ return self._validate(content, allowed_extra_fields=self._property_fields_set)
def get_bound_form(self, content=None):
@@ -131,23 +143,50 @@ class ModelFormValidatorMixin(FormValidatorMixin):
If the form class attribute has been explicitly set then use that class to create a Form,
otherwise if model is set use that class to create a ModelForm, otherwise return None."""
+
if self.form:
# Use explict Form
return super(ModelFormValidatorMixin, self).get_bound_form(content)
elif self.model:
# Fall back to ModelForm which we create on the fly
- class ModelForm(forms.ModelForm):
+ class OnTheFlyModelForm(forms.ModelForm):
class Meta:
model = self.model
- fields = tuple(set.intersection(self.model._meta.fields, self.fields))
-
+ #fields = tuple(self._model_fields_set)
+
# Instantiate the ModelForm as appropriate
if content and isinstance(content, models.Model):
- return ModelForm(instance=content)
+ return OnTheFlyModelForm(instance=content)
elif content:
- return ModelForm(content)
- return ModelForm()
+ return OnTheFlyModelForm(content)
+ return OnTheFlyModelForm()
# Both form and model not set? Okay bruv, whatevs...
- return None \ No newline at end of file
+ return None
+
+
+ @property
+ def _model_fields_set(self):
+ """Return a set containing the names of validated fields on the model."""
+ model_fields = set(field.name for field in self.model._meta.fields)
+
+ if self.fields:
+ return model_fields & set(as_tuple(self.fields))
+
+ return model_fields - set(as_tuple(self.exclude_fields))
+
+ @property
+ def _property_fields_set(self):
+ """Returns a set containing the names of validated properties on the model."""
+ property_fields = set(attr for attr in dir(self.model) if
+ isinstance(getattr(self.model, attr, None), property)
+ and not attr.startswith('_'))
+
+ if self.fields:
+ return property_fields & set(as_tuple(self.fields))
+
+ return property_fields - set(as_tuple(self.exclude_fields))
+
+
+ \ No newline at end of file