mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 07:57:55 +03:00 
			
		
		
		
	Remove validators (logic into resources)
This commit is contained in:
		
							parent
							
								
									80d7d5be67
								
							
						
					
					
						commit
						3f6b6e437b
					
				|  | @ -1,228 +0,0 @@ | |||
| """Mixin classes that provide a validate(content) function to validate and cleanup request content""" | ||||
| from django import forms | ||||
| from django.db import models | ||||
| from djangorestframework.response import ErrorResponse | ||||
| from djangorestframework.utils import as_tuple | ||||
| 
 | ||||
| 
 | ||||
| class BaseValidator(object): | ||||
|     """Base class for all Validator classes, which simply defines the interface they provide.""" | ||||
| 
 | ||||
|     def __init__(self, view): | ||||
|         self.view = view | ||||
| 
 | ||||
|     def validate(self, content): | ||||
|         """Given some content as input return some cleaned, validated content. | ||||
|         Typically raises a ErrorResponse with status code 400 (Bad Request) on failure. | ||||
| 
 | ||||
|         Must be overridden to be implemented.""" | ||||
|         raise NotImplementedError() | ||||
| 
 | ||||
| 
 | ||||
| class FormValidator(BaseValidator): | ||||
|     """Validator class that uses forms for validation. | ||||
|     Also provides a get_bound_form() method which may be used by some renderers. | ||||
|      | ||||
|     The view class should provide `.form` attribute which specifies the form classmethod | ||||
|     to be used for validation. | ||||
|      | ||||
|     On calling validate() this validator may set a `.bound_form_instance` attribute on the | ||||
|     view, which may be used by some renderers.""" | ||||
| 
 | ||||
| 
 | ||||
|     def validate(self, content): | ||||
|         """ | ||||
|         Given some content as input return some cleaned, validated content. | ||||
|         Raises a ErrorResponse with status code 400 (Bad Request) on failure. | ||||
|          | ||||
|         Validation is standard form validation, with an additional constraint that no extra unknown fields may be supplied. | ||||
| 
 | ||||
|         On failure the ErrorResponse 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}. | ||||
|         """ | ||||
|         return self._validate(content) | ||||
| 
 | ||||
|     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. | ||||
|         """ | ||||
|         bound_form = self.get_bound_form(content) | ||||
| 
 | ||||
|         if bound_form is None: | ||||
|             return content | ||||
|          | ||||
|         self.view.bound_form_instance = bound_form | ||||
| 
 | ||||
|         seen_fields_set = set(content.keys()) | ||||
|         form_fields_set = set(bound_form.fields.keys()) | ||||
|         allowed_extra_fields_set = set(allowed_extra_fields) | ||||
| 
 | ||||
|         # 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: | ||||
|             # Validation succeeded... | ||||
|             cleaned_data = bound_form.cleaned_data | ||||
| 
 | ||||
|             cleaned_data.update(bound_form.files) | ||||
| 
 | ||||
|             # 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: | ||||
|             detail = {u'errors': [u'No content was supplied.']} | ||||
| 
 | ||||
|         else:        | ||||
|             # Add any non-field errors | ||||
|             if bound_form.non_field_errors(): | ||||
|                 detail[u'errors'] = bound_form.non_field_errors() | ||||
| 
 | ||||
|             # Add standard field errors | ||||
|             field_errors = dict((key, map(unicode, val)) | ||||
|                 for (key, val) | ||||
|                 in bound_form.errors.iteritems() | ||||
|                 if not key.startswith('__')) | ||||
| 
 | ||||
|             # Add any unknown field errors | ||||
|             for key in unknown_fields: | ||||
|                 field_errors[key] = [u'This field does not exist.'] | ||||
|         | ||||
|             if field_errors: | ||||
|                 detail[u'field-errors'] = field_errors | ||||
| 
 | ||||
|         # Return HTTP 400 response (BAD REQUEST) | ||||
|         raise ErrorResponse(400, detail) | ||||
|    | ||||
| 
 | ||||
|     def get_bound_form(self, content=None): | ||||
|         """Given some content return a Django form bound to that content. | ||||
|         If form validation is turned off (form class attribute is None) then returns None.""" | ||||
|         form_cls = getattr(self.view, 'form', None) | ||||
| 
 | ||||
|         if not form_cls: | ||||
|             return None | ||||
| 
 | ||||
|         if content is not None: | ||||
|             if hasattr(content, 'FILES'): | ||||
|                 return form_cls(content, content.FILES) | ||||
|             return form_cls(content) | ||||
|         return form_cls() | ||||
| 
 | ||||
| 
 | ||||
| class ModelFormValidator(FormValidator): | ||||
|     """Validator class that uses forms for validation and otherwise falls back to a model form if no form is set. | ||||
|     Also provides a get_bound_form() method which may be used by some renderers.""" | ||||
|   | ||||
|     """The form class that should be used for validation, or None to use model form validation."""    | ||||
|     form = None | ||||
|      | ||||
|     """The model class from which the model form should be constructed if no form is set.""" | ||||
|     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. | ||||
| 
 | ||||
|     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.) | ||||
|     def validate(self, content): | ||||
|         """ | ||||
|         Given some content as input return some cleaned, validated content. | ||||
|         Raises a ErrorResponse with status code 400 (Bad Request) on failure. | ||||
|          | ||||
|         Validation is standard form or model form validation, | ||||
|         with an additional constraint that no extra unknown fields may be supplied, | ||||
|         and that all fields specified by the fields class attribute must be supplied, | ||||
|         even if they are not validated by the form/model form. | ||||
| 
 | ||||
|         On failure the ErrorResponse 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}. | ||||
|         """ | ||||
|         return self._validate(content, allowed_extra_fields=self._property_fields_set) | ||||
| 
 | ||||
| 
 | ||||
|     def get_bound_form(self, content=None): | ||||
|         """Given some content return a Django form bound to that content. | ||||
| 
 | ||||
|         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.""" | ||||
| 
 | ||||
|         form_cls = getattr(self.view, 'form', None) | ||||
|         model_cls = getattr(self.view.resource, 'model', None) | ||||
| 
 | ||||
|         if form_cls: | ||||
|             # Use explict Form | ||||
|             return super(ModelFormValidator, self).get_bound_form(content) | ||||
| 
 | ||||
|         elif model_cls: | ||||
|             # Fall back to ModelForm which we create on the fly | ||||
|             class OnTheFlyModelForm(forms.ModelForm): | ||||
|                 class Meta: | ||||
|                     model = model_cls | ||||
|                     #fields = tuple(self._model_fields_set) | ||||
| 
 | ||||
|             # Instantiate the ModelForm as appropriate | ||||
|             if content and isinstance(content, models.Model): | ||||
|                 # Bound to an existing model instance | ||||
|                 return OnTheFlyModelForm(instance=content) | ||||
|             elif not content is None: | ||||
|                 if hasattr(content, 'FILES'): | ||||
|                     return OnTheFlyModelForm(content, content.FILES) | ||||
|                 return OnTheFlyModelForm(content) | ||||
|             return OnTheFlyModelForm() | ||||
| 
 | ||||
|         # Both form and model not set?  Okay bruv, whatevs... | ||||
|         return None | ||||
|      | ||||
| 
 | ||||
|     @property | ||||
|     def _model_fields_set(self): | ||||
|         """Return a set containing the names of validated fields on the model.""" | ||||
|         resource = self.view.resource | ||||
|         model = getattr(resource, 'model', None) | ||||
|         fields = getattr(resource, 'fields', self.fields) | ||||
|         exclude_fields = getattr(resource, 'exclude_fields', self.exclude_fields) | ||||
| 
 | ||||
|         model_fields = set(field.name for field in model._meta.fields) | ||||
| 
 | ||||
|         if fields: | ||||
|             return model_fields & set(as_tuple(fields)) | ||||
| 
 | ||||
|         return model_fields - set(as_tuple(exclude_fields)) | ||||
|      | ||||
|     @property | ||||
|     def _property_fields_set(self): | ||||
|         """Returns a set containing the names of validated properties on the model.""" | ||||
|         resource = self.view.resource | ||||
|         model = getattr(resource, 'model', None) | ||||
|         fields = getattr(resource, 'fields', self.fields) | ||||
|         exclude_fields = getattr(resource, 'exclude_fields', self.exclude_fields) | ||||
| 
 | ||||
|         property_fields = set(attr for attr in dir(model) if | ||||
|                               isinstance(getattr(model, attr, None), property) | ||||
|                               and not attr.startswith('_')) | ||||
| 
 | ||||
|         if fields: | ||||
|             return property_fields & set(as_tuple(fields)) | ||||
| 
 | ||||
|         return property_fields - set(as_tuple(exclude_fields)) | ||||
|      | ||||
| 
 | ||||
|      | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user