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