mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-10 19:56:59 +03:00
parent
bc87bf13b4
commit
bba918fb2a
|
@ -543,6 +543,7 @@ class ModelSerializer(Serializer):
|
|||
opts = get_concrete_model(cls)._meta
|
||||
exclusions = [field.name for field in opts.fields + opts.many_to_many]
|
||||
for field_name, field in self.fields.items():
|
||||
field_name = field.source or field_name
|
||||
if field_name in exclusions and not field.read_only:
|
||||
exclusions.remove(field_name)
|
||||
return exclusions
|
||||
|
|
|
@ -350,35 +350,3 @@ class TestM2MBrowseableAPI(TestCase):
|
|||
view = ExampleView().as_view()
|
||||
response = view(request).render()
|
||||
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
# Regression for #666
|
||||
|
||||
class ValidationModel(models.Model):
|
||||
blank_validated_field = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class ValidationModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ValidationModel
|
||||
fields = ('blank_validated_field',)
|
||||
read_only_fields = ('blank_validated_field',)
|
||||
|
||||
|
||||
class UpdateValidationModel(generics.RetrieveUpdateDestroyAPIView):
|
||||
model = ValidationModel
|
||||
serializer_class = ValidationModelSerializer
|
||||
|
||||
|
||||
class TestPreSaveValidationExclusions(TestCase):
|
||||
def test_pre_save_validation_exclusions(self):
|
||||
"""
|
||||
Somewhat weird test case to ensure that we don't perform model
|
||||
validation on read only fields.
|
||||
"""
|
||||
obj = ValidationModel.objects.create(blank_validated_field='')
|
||||
request = factory.put('/', json.dumps({}),
|
||||
content_type='application/json')
|
||||
view = UpdateValidationModel().as_view()
|
||||
response = view(request, pk=obj.pk).render()
|
||||
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
||||
|
|
65
rest_framework/tests/validation.py
Normal file
65
rest_framework/tests/validation.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from __future__ import unicode_literals
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from rest_framework import generics, serializers, status
|
||||
from rest_framework.tests.utils import RequestFactory
|
||||
import json
|
||||
|
||||
factory = RequestFactory()
|
||||
|
||||
|
||||
# Regression for #666
|
||||
|
||||
class ValidationModel(models.Model):
|
||||
blank_validated_field = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class ValidationModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ValidationModel
|
||||
fields = ('blank_validated_field',)
|
||||
read_only_fields = ('blank_validated_field',)
|
||||
|
||||
|
||||
class UpdateValidationModel(generics.RetrieveUpdateDestroyAPIView):
|
||||
model = ValidationModel
|
||||
serializer_class = ValidationModelSerializer
|
||||
|
||||
|
||||
class TestPreSaveValidationExclusions(TestCase):
|
||||
def test_pre_save_validation_exclusions(self):
|
||||
"""
|
||||
Somewhat weird test case to ensure that we don't perform model
|
||||
validation on read only fields.
|
||||
"""
|
||||
obj = ValidationModel.objects.create(blank_validated_field='')
|
||||
request = factory.put('/', json.dumps({}),
|
||||
content_type='application/json')
|
||||
view = UpdateValidationModel().as_view()
|
||||
response = view(request, pk=obj.pk).render()
|
||||
self.assertEquals(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
# Regression for #653
|
||||
|
||||
class ShouldValidateModel(models.Model):
|
||||
should_validate_field = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class ShouldValidateModelSerializer(serializers.ModelSerializer):
|
||||
renamed = serializers.CharField(source='should_validate_field', required=False)
|
||||
|
||||
class Meta:
|
||||
model = ShouldValidateModel
|
||||
fields = ('renamed',)
|
||||
|
||||
|
||||
class TestPreSaveValidationExclusions(TestCase):
|
||||
def test_renamed_fields_are_model_validated(self):
|
||||
"""
|
||||
Ensure fields with 'source' applied do get still get model validation.
|
||||
"""
|
||||
# We've set `required=False` on the serializer, but the model
|
||||
# does not have `blank=True`, so this serializer should not validate.
|
||||
serializer = ShouldValidateModelSerializer(data={'renamed': ''})
|
||||
self.assertEquals(serializer.is_valid(), False)
|
|
@ -1,329 +0,0 @@
|
|||
# from django import forms
|
||||
# from django.db import models
|
||||
# from django.test import TestCase
|
||||
# from rest_framework.response import ImmediateResponse
|
||||
# from rest_framework.views import View
|
||||
|
||||
|
||||
# class TestDisabledValidations(TestCase):
|
||||
# """Tests on FormValidator with validation disabled by setting form to None"""
|
||||
|
||||
# def test_disabled_form_validator_returns_content_unchanged(self):
|
||||
# """If the view's form attribute is None then FormValidator(view).validate_request(content, None)
|
||||
# should just return the content unmodified."""
|
||||
# class DisabledFormResource(FormResource):
|
||||
# form = None
|
||||
|
||||
# class MockView(View):
|
||||
# resource = DisabledFormResource
|
||||
|
||||
# view = MockView()
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(FormResource(view).validate_request(content, None), content)
|
||||
|
||||
# def test_disabled_form_validator_get_bound_form_returns_none(self):
|
||||
# """If the view's form attribute is None on then
|
||||
# FormValidator(view).get_bound_form(content) should just return None."""
|
||||
# class DisabledFormResource(FormResource):
|
||||
# form = None
|
||||
|
||||
# class MockView(View):
|
||||
# resource = DisabledFormResource
|
||||
|
||||
# view = MockView()
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(FormResource(view).get_bound_form(content), None)
|
||||
|
||||
# def test_disabled_model_form_validator_returns_content_unchanged(self):
|
||||
# """If the view's form is None and does not have a Resource with a model set then
|
||||
# ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
|
||||
|
||||
# class DisabledModelFormView(View):
|
||||
# resource = ModelResource
|
||||
|
||||
# view = DisabledModelFormView()
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(ModelResource(view).get_bound_form(content), None)
|
||||
|
||||
# def test_disabled_model_form_validator_get_bound_form_returns_none(self):
|
||||
# """If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
|
||||
# class DisabledModelFormView(View):
|
||||
# resource = ModelResource
|
||||
|
||||
# view = DisabledModelFormView()
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(ModelResource(view).get_bound_form(content), None)
|
||||
|
||||
|
||||
# class TestNonFieldErrors(TestCase):
|
||||
# """Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
|
||||
|
||||
# def test_validate_failed_due_to_non_field_error_returns_appropriate_message(self):
|
||||
# """If validation fails with a non-field error, ensure the response a non-field error"""
|
||||
# class MockForm(forms.Form):
|
||||
# field1 = forms.CharField(required=False)
|
||||
# field2 = forms.CharField(required=False)
|
||||
# ERROR_TEXT = 'You may not supply both field1 and field2'
|
||||
|
||||
# def clean(self):
|
||||
# if 'field1' in self.cleaned_data and 'field2' in self.cleaned_data:
|
||||
# raise forms.ValidationError(self.ERROR_TEXT)
|
||||
# return self.cleaned_data
|
||||
|
||||
# class MockResource(FormResource):
|
||||
# form = MockForm
|
||||
|
||||
# class MockView(View):
|
||||
# pass
|
||||
|
||||
# view = MockView()
|
||||
# content = {'field1': 'example1', 'field2': 'example2'}
|
||||
# try:
|
||||
# MockResource(view).validate_request(content, None)
|
||||
# except ImmediateResponse, exc:
|
||||
# response = exc.response
|
||||
# self.assertEqual(response.raw_content, {'errors': [MockForm.ERROR_TEXT]})
|
||||
# else:
|
||||
# self.fail('ImmediateResponse was not raised')
|
||||
|
||||
|
||||
# class TestFormValidation(TestCase):
|
||||
# """Tests which check basic form validation.
|
||||
# Also includes the same set of tests with a ModelFormValidator for which the form has been explicitly set.
|
||||
# (ModelFormValidator should behave as FormValidator if a form is set rather than relying on the default ModelForm)"""
|
||||
# def setUp(self):
|
||||
# class MockForm(forms.Form):
|
||||
# qwerty = forms.CharField(required=True)
|
||||
|
||||
# class MockFormResource(FormResource):
|
||||
# form = MockForm
|
||||
|
||||
# class MockModelResource(ModelResource):
|
||||
# form = MockForm
|
||||
|
||||
# class MockFormView(View):
|
||||
# resource = MockFormResource
|
||||
|
||||
# class MockModelFormView(View):
|
||||
# resource = MockModelResource
|
||||
|
||||
# self.MockFormResource = MockFormResource
|
||||
# self.MockModelResource = MockModelResource
|
||||
# self.MockFormView = MockFormView
|
||||
# self.MockModelFormView = MockModelFormView
|
||||
|
||||
# def validation_returns_content_unchanged_if_already_valid_and_clean(self, validator):
|
||||
# """If the content is already valid and clean then validate(content) should just return the content unmodified."""
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(validator.validate_request(content, None), content)
|
||||
|
||||
# def validation_failure_raises_response_exception(self, validator):
|
||||
# """If form validation fails a ResourceException 400 (Bad Request) should be raised."""
|
||||
# content = {}
|
||||
# self.assertRaises(ImmediateResponse, validator.validate_request, content, None)
|
||||
|
||||
# def validation_does_not_allow_extra_fields_by_default(self, validator):
|
||||
# """If some (otherwise valid) content includes fields that are not in the form then validation should fail.
|
||||
# It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
|
||||
# broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
# content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
# self.assertRaises(ImmediateResponse, validator.validate_request, content, None)
|
||||
|
||||
# def validation_allows_extra_fields_if_explicitly_set(self, validator):
|
||||
# """If we include an allowed_extra_fields paramater on _validate, then allow fields with those names."""
|
||||
# content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
# validator._validate(content, None, allowed_extra_fields=('extra',))
|
||||
|
||||
# def validation_allows_unknown_fields_if_explicitly_allowed(self, validator):
|
||||
# """If we set ``unknown_form_fields`` on the form resource, then don't
|
||||
# raise errors on unexpected request data"""
|
||||
# content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
# validator.allow_unknown_form_fields = True
|
||||
# self.assertEqual({'qwerty': 'uiop'},
|
||||
# validator.validate_request(content, None),
|
||||
# "Resource didn't accept unknown fields.")
|
||||
# validator.allow_unknown_form_fields = False
|
||||
|
||||
# def validation_does_not_require_extra_fields_if_explicitly_set(self, validator):
|
||||
# """If we include an allowed_extra_fields paramater on _validate, then do not fail if we do not have fields with those names."""
|
||||
# content = {'qwerty': 'uiop'}
|
||||
# self.assertEqual(validator._validate(content, None, allowed_extra_fields=('extra',)), content)
|
||||
|
||||
# def validation_failed_due_to_no_content_returns_appropriate_message(self, validator):
|
||||
# """If validation fails due to no content, ensure the response contains a single non-field error"""
|
||||
# content = {}
|
||||
# try:
|
||||
# validator.validate_request(content, None)
|
||||
# except ImmediateResponse, exc:
|
||||
# response = exc.response
|
||||
# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
|
||||
# else:
|
||||
# self.fail('ResourceException was not raised')
|
||||
|
||||
# def validation_failed_due_to_field_error_returns_appropriate_message(self, validator):
|
||||
# """If validation fails due to a field error, ensure the response contains a single field error"""
|
||||
# content = {'qwerty': ''}
|
||||
# try:
|
||||
# validator.validate_request(content, None)
|
||||
# except ImmediateResponse, exc:
|
||||
# response = exc.response
|
||||
# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.']}})
|
||||
# else:
|
||||
# self.fail('ResourceException was not raised')
|
||||
|
||||
# def validation_failed_due_to_invalid_field_returns_appropriate_message(self, validator):
|
||||
# """If validation fails due to an invalid field, ensure the response contains a single field error"""
|
||||
# content = {'qwerty': 'uiop', 'extra': 'extra'}
|
||||
# try:
|
||||
# validator.validate_request(content, None)
|
||||
# except ImmediateResponse, exc:
|
||||
# response = exc.response
|
||||
# self.assertEqual(response.raw_content, {'field_errors': {'extra': ['This field does not exist.']}})
|
||||
# else:
|
||||
# self.fail('ResourceException was not raised')
|
||||
|
||||
# def validation_failed_due_to_multiple_errors_returns_appropriate_message(self, validator):
|
||||
# """If validation for multiple reasons, ensure the response contains each error"""
|
||||
# content = {'qwerty': '', 'extra': 'extra'}
|
||||
# try:
|
||||
# validator.validate_request(content, None)
|
||||
# except ImmediateResponse, exc:
|
||||
# response = exc.response
|
||||
# self.assertEqual(response.raw_content, {'field_errors': {'qwerty': ['This field is required.'],
|
||||
# 'extra': ['This field does not exist.']}})
|
||||
# else:
|
||||
# self.fail('ResourceException was not raised')
|
||||
|
||||
# # Tests on FormResource
|
||||
|
||||
# def test_form_validation_returns_content_unchanged_if_already_valid_and_clean(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
|
||||
|
||||
# def test_form_validation_failure_raises_response_exception(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_failure_raises_response_exception(validator)
|
||||
|
||||
# def test_validation_does_not_allow_extra_fields_by_default(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_does_not_allow_extra_fields_by_default(validator)
|
||||
|
||||
# def test_validation_allows_extra_fields_if_explicitly_set(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_allows_extra_fields_if_explicitly_set(validator)
|
||||
|
||||
# def test_validation_allows_unknown_fields_if_explicitly_allowed(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_allows_unknown_fields_if_explicitly_allowed(validator)
|
||||
|
||||
# def test_validation_does_not_require_extra_fields_if_explicitly_set(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
|
||||
|
||||
# def test_validation_failed_due_to_no_content_returns_appropriate_message(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
|
||||
|
||||
# def test_validation_failed_due_to_field_error_returns_appropriate_message(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
|
||||
|
||||
# def test_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
|
||||
|
||||
# def test_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
|
||||
# validator = self.MockFormResource(self.MockFormView())
|
||||
# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
|
||||
|
||||
# # Same tests on ModelResource
|
||||
|
||||
# def test_modelform_validation_returns_content_unchanged_if_already_valid_and_clean(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_returns_content_unchanged_if_already_valid_and_clean(validator)
|
||||
|
||||
# def test_modelform_validation_failure_raises_response_exception(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_failure_raises_response_exception(validator)
|
||||
|
||||
# def test_modelform_validation_does_not_allow_extra_fields_by_default(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_does_not_allow_extra_fields_by_default(validator)
|
||||
|
||||
# def test_modelform_validation_allows_extra_fields_if_explicitly_set(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_allows_extra_fields_if_explicitly_set(validator)
|
||||
|
||||
# def test_modelform_validation_does_not_require_extra_fields_if_explicitly_set(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_does_not_require_extra_fields_if_explicitly_set(validator)
|
||||
|
||||
# def test_modelform_validation_failed_due_to_no_content_returns_appropriate_message(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_failed_due_to_no_content_returns_appropriate_message(validator)
|
||||
|
||||
# def test_modelform_validation_failed_due_to_field_error_returns_appropriate_message(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_failed_due_to_field_error_returns_appropriate_message(validator)
|
||||
|
||||
# def test_modelform_validation_failed_due_to_invalid_field_returns_appropriate_message(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_failed_due_to_invalid_field_returns_appropriate_message(validator)
|
||||
|
||||
# def test_modelform_validation_failed_due_to_multiple_errors_returns_appropriate_message(self):
|
||||
# validator = self.MockModelResource(self.MockModelFormView())
|
||||
# self.validation_failed_due_to_multiple_errors_returns_appropriate_message(validator)
|
||||
|
||||
|
||||
# class TestModelFormValidator(TestCase):
|
||||
# """Tests specific to ModelFormValidatorMixin"""
|
||||
|
||||
# def setUp(self):
|
||||
# """Create a validator for a model with two fields and a property."""
|
||||
# class MockModel(models.Model):
|
||||
# qwerty = models.CharField(max_length=256)
|
||||
# uiop = models.CharField(max_length=256, blank=True)
|
||||
|
||||
# @property
|
||||
# def read_only(self):
|
||||
# return 'read only'
|
||||
|
||||
# class MockResource(ModelResource):
|
||||
# model = MockModel
|
||||
|
||||
# class MockView(View):
|
||||
# resource = MockResource
|
||||
|
||||
# self.validator = MockResource(MockView)
|
||||
|
||||
# def test_property_fields_are_allowed_on_model_forms(self):
|
||||
# """Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
|
||||
# content = {'qwerty': 'example', 'uiop': 'example', 'read_only': 'read only'}
|
||||
# self.assertEqual(self.validator.validate_request(content, None), content)
|
||||
|
||||
# def test_property_fields_are_not_required_on_model_forms(self):
|
||||
# """Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
|
||||
# content = {'qwerty': 'example', 'uiop': 'example'}
|
||||
# self.assertEqual(self.validator.validate_request(content, None), content)
|
||||
|
||||
# def test_extra_fields_not_allowed_on_model_forms(self):
|
||||
# """If some (otherwise valid) content includes fields that are not in the form then validation should fail.
|
||||
# It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
|
||||
# broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
# content = {'qwerty': 'example', 'uiop': 'example', 'read_only': 'read only', 'extra': 'extra'}
|
||||
# self.assertRaises(ImmediateResponse, self.validator.validate_request, content, None)
|
||||
|
||||
# def test_validate_requires_fields_on_model_forms(self):
|
||||
# """If some (otherwise valid) content includes fields that are not in the form then validation should fail.
|
||||
# It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
|
||||
# broken clients more easily (eg submitting content with a misnamed field)"""
|
||||
# content = {'read_only': 'read only'}
|
||||
# self.assertRaises(ImmediateResponse, self.validator.validate_request, content, None)
|
||||
|
||||
# def test_validate_does_not_require_blankable_fields_on_model_forms(self):
|
||||
# """Test standard ModelForm validation behaviour - fields with blank=True are not required."""
|
||||
# content = {'qwerty': 'example', 'read_only': 'read only'}
|
||||
# self.validator.validate_request(content, None)
|
||||
|
||||
# def test_model_form_validator_uses_model_forms(self):
|
||||
# self.assertTrue(isinstance(self.validator.get_bound_form(), forms.ModelForm))
|
Loading…
Reference in New Issue
Block a user