aboutsummaryrefslogtreecommitdiffstats
path: root/djangorestframework/validators.py
diff options
context:
space:
mode:
authortom christie tom@tomchristie.com2011-02-19 10:26:27 +0000
committertom christie tom@tomchristie.com2011-02-19 10:26:27 +0000
commit805aa03ec1871f6a766d9052b348ddce9e9843c3 (patch)
tree8ab5b6a7396236aa45bbc61e8404cc77fc75a9c5 /djangorestframework/validators.py
parentb749b950a1b4bede76b7e3900a6385779904902d (diff)
downloaddjango-rest-framework-805aa03ec1871f6a766d9052b348ddce9e9843c3.tar.bz2
Yowzers. Final big bunch of refactoring for 0.1 release. Now support Django 1.3's views, admin style api is all polished off, loads of tests, new test project for running the test. All sorts of goodness. Getting ready to push this out now.
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