diff options
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/serializers.py | 1 | ||||
| -rw-r--r-- | rest_framework/tests/generics.py | 32 | ||||
| -rw-r--r-- | rest_framework/tests/validation.py | 65 | ||||
| -rw-r--r-- | rest_framework/tests/validators.py | 329 |
4 files changed, 66 insertions, 361 deletions
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 89b3d8ce..764313f7 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -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 diff --git a/rest_framework/tests/generics.py b/rest_framework/tests/generics.py index 2dcf0106..adbd6253 100644 --- a/rest_framework/tests/generics.py +++ b/rest_framework/tests/generics.py @@ -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) diff --git a/rest_framework/tests/validation.py b/rest_framework/tests/validation.py new file mode 100644 index 00000000..4d46cbdc --- /dev/null +++ b/rest_framework/tests/validation.py @@ -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) diff --git a/rest_framework/tests/validators.py b/rest_framework/tests/validators.py deleted file mode 100644 index 8844cb74..00000000 --- a/rest_framework/tests/validators.py +++ /dev/null @@ -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)) |
