mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
a3802504a0
Add error codes to `APIException`
252 lines
8.2 KiB
Python
252 lines
8.2 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import re
|
|
|
|
from django.core.validators import MaxValueValidator, RegexValidator
|
|
from django.db import models
|
|
from django.test import TestCase
|
|
|
|
from rest_framework import generics, serializers, status
|
|
from rest_framework.test import APIRequestFactory
|
|
|
|
factory = APIRequestFactory()
|
|
|
|
|
|
# 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):
|
|
queryset = ValidationModel.objects.all()
|
|
serializer_class = ValidationModelSerializer
|
|
|
|
|
|
# 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)
|
|
|
|
def validate_renamed(self, value):
|
|
if len(value) < 3:
|
|
raise serializers.ValidationError('Minimum 3 characters.')
|
|
return value
|
|
|
|
class Meta:
|
|
model = ShouldValidateModel
|
|
fields = ('renamed',)
|
|
|
|
|
|
class TestNestedValidationError(TestCase):
|
|
def test_nested_validation_error_detail(self):
|
|
"""
|
|
Ensure nested validation error detail is rendered correctly.
|
|
"""
|
|
e = serializers.ValidationError({
|
|
'nested': {
|
|
'field': ['error'],
|
|
}
|
|
})
|
|
|
|
self.assertEqual(serializers.as_serializer_error(e), {
|
|
'nested': {
|
|
'field': ['error'],
|
|
}
|
|
})
|
|
|
|
|
|
class TestPreSaveValidationExclusionsSerializer(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.assertEqual(serializer.is_valid(), False)
|
|
self.assertIn('renamed', serializer.errors)
|
|
self.assertNotIn('should_validate_field', serializer.errors)
|
|
|
|
|
|
class TestCustomValidationMethods(TestCase):
|
|
def test_custom_validation_method_is_executed(self):
|
|
serializer = ShouldValidateModelSerializer(data={'renamed': 'fo'})
|
|
self.assertFalse(serializer.is_valid())
|
|
self.assertIn('renamed', serializer.errors)
|
|
|
|
def test_custom_validation_method_passing(self):
|
|
serializer = ShouldValidateModelSerializer(data={'renamed': 'foo'})
|
|
self.assertTrue(serializer.is_valid())
|
|
|
|
|
|
class ValidationSerializer(serializers.Serializer):
|
|
foo = serializers.CharField()
|
|
|
|
def validate_foo(self, attrs, source):
|
|
raise serializers.ValidationError("foo invalid")
|
|
|
|
def validate(self, attrs):
|
|
raise serializers.ValidationError("serializer invalid")
|
|
|
|
|
|
class TestAvoidValidation(TestCase):
|
|
"""
|
|
If serializer was initialized with invalid data (None or non dict-like), it
|
|
should avoid validation layer (validate_<field> and validate methods)
|
|
"""
|
|
def test_serializer_errors_has_only_invalid_data_error(self):
|
|
serializer = ValidationSerializer(data='invalid data')
|
|
self.assertFalse(serializer.is_valid())
|
|
self.assertDictEqual(serializer.errors, {
|
|
'non_field_errors': [
|
|
'Invalid data. Expected a dictionary, but got %s.' % type('').__name__
|
|
]
|
|
})
|
|
|
|
|
|
# regression tests for issue: 1493
|
|
|
|
class ValidationMaxValueValidatorModel(models.Model):
|
|
number_value = models.PositiveIntegerField(validators=[MaxValueValidator(100)])
|
|
|
|
|
|
class ValidationMaxValueValidatorModelSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = ValidationMaxValueValidatorModel
|
|
fields = '__all__'
|
|
|
|
|
|
class UpdateMaxValueValidationModel(generics.RetrieveUpdateDestroyAPIView):
|
|
queryset = ValidationMaxValueValidatorModel.objects.all()
|
|
serializer_class = ValidationMaxValueValidatorModelSerializer
|
|
|
|
|
|
class TestMaxValueValidatorValidation(TestCase):
|
|
|
|
def test_max_value_validation_serializer_success(self):
|
|
serializer = ValidationMaxValueValidatorModelSerializer(data={'number_value': 99})
|
|
self.assertTrue(serializer.is_valid())
|
|
|
|
def test_max_value_validation_serializer_fails(self):
|
|
serializer = ValidationMaxValueValidatorModelSerializer(data={'number_value': 101})
|
|
self.assertFalse(serializer.is_valid())
|
|
self.assertDictEqual({'number_value': ['Ensure this value is less than or equal to 100.']}, serializer.errors)
|
|
|
|
def test_max_value_validation_success(self):
|
|
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
|
request = factory.patch('/{0}'.format(obj.pk), {'number_value': 98}, format='json')
|
|
view = UpdateMaxValueValidationModel().as_view()
|
|
response = view(request, pk=obj.pk).render()
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
def test_max_value_validation_fail(self):
|
|
obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
|
|
request = factory.patch('/{0}'.format(obj.pk), {'number_value': 101}, format='json')
|
|
view = UpdateMaxValueValidationModel().as_view()
|
|
response = view(request, pk=obj.pk).render()
|
|
self.assertEqual(response.content, b'{"number_value":["Ensure this value is less than or equal to 100."]}')
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
# regression tests for issue: 1533
|
|
|
|
class TestChoiceFieldChoicesValidate(TestCase):
|
|
CHOICES = [
|
|
(0, 'Small'),
|
|
(1, 'Medium'),
|
|
(2, 'Large'),
|
|
]
|
|
|
|
SINGLE_CHOICES = [0, 1, 2]
|
|
|
|
CHOICES_NESTED = [
|
|
('Category', (
|
|
(1, 'First'),
|
|
(2, 'Second'),
|
|
(3, 'Third'),
|
|
)),
|
|
(4, 'Fourth'),
|
|
]
|
|
|
|
MIXED_CHOICES = [
|
|
('Category', (
|
|
(1, 'First'),
|
|
(2, 'Second'),
|
|
)),
|
|
3,
|
|
(4, 'Fourth'),
|
|
]
|
|
|
|
def test_choices(self):
|
|
"""
|
|
Make sure a value for choices works as expected.
|
|
"""
|
|
f = serializers.ChoiceField(choices=self.CHOICES)
|
|
value = self.CHOICES[0][0]
|
|
try:
|
|
f.to_internal_value(value)
|
|
except serializers.ValidationError:
|
|
self.fail("Value %s does not validate" % str(value))
|
|
|
|
def test_single_choices(self):
|
|
"""
|
|
Make sure a single value for choices works as expected.
|
|
"""
|
|
f = serializers.ChoiceField(choices=self.SINGLE_CHOICES)
|
|
value = self.SINGLE_CHOICES[0]
|
|
try:
|
|
f.to_internal_value(value)
|
|
except serializers.ValidationError:
|
|
self.fail("Value %s does not validate" % str(value))
|
|
|
|
def test_nested_choices(self):
|
|
"""
|
|
Make sure a nested value for choices works as expected.
|
|
"""
|
|
f = serializers.ChoiceField(choices=self.CHOICES_NESTED)
|
|
value = self.CHOICES_NESTED[0][1][0][0]
|
|
try:
|
|
f.to_internal_value(value)
|
|
except serializers.ValidationError:
|
|
self.fail("Value %s does not validate" % str(value))
|
|
|
|
def test_mixed_choices(self):
|
|
"""
|
|
Make sure mixed values for choices works as expected.
|
|
"""
|
|
f = serializers.ChoiceField(choices=self.MIXED_CHOICES)
|
|
value = self.MIXED_CHOICES[1]
|
|
try:
|
|
f.to_internal_value(value)
|
|
except serializers.ValidationError:
|
|
self.fail("Value %s does not validate" % str(value))
|
|
|
|
|
|
class RegexSerializer(serializers.Serializer):
|
|
pin = serializers.CharField(
|
|
validators=[RegexValidator(regex=re.compile('^[0-9]{4,6}$'),
|
|
message='A PIN is 4-6 digits')])
|
|
|
|
expected_repr = """
|
|
RegexSerializer():
|
|
pin = CharField(validators=[<django.core.validators.RegexValidator object>])
|
|
""".strip()
|
|
|
|
|
|
class TestRegexSerializer(TestCase):
|
|
def test_regex_repr(self):
|
|
serializer_repr = repr(RegexSerializer())
|
|
assert serializer_repr == expected_repr
|