mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	Thanks to Jon Dufresne (@jdufresne) for review. Co-authored-by: Asif Saif Uddin <auvipy@gmail.com> Co-authored-by: Rizwan Mansuri <Rizwan@webbyfox.com>
		
			
				
	
	
		
			255 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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'],
 | |
|             }
 | |
|         })
 | |
| 
 | |
|         assert 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': ''})
 | |
|         assert serializer.is_valid() is False
 | |
|         assert 'renamed' in serializer.errors
 | |
|         assert 'should_validate_field' not in serializer.errors
 | |
| 
 | |
| 
 | |
| class TestCustomValidationMethods(TestCase):
 | |
|     def test_custom_validation_method_is_executed(self):
 | |
|         serializer = ShouldValidateModelSerializer(data={'renamed': 'fo'})
 | |
|         assert not serializer.is_valid()
 | |
|         assert 'renamed' in serializer.errors
 | |
| 
 | |
|     def test_custom_validation_method_passing(self):
 | |
|         serializer = ShouldValidateModelSerializer(data={'renamed': 'foo'})
 | |
|         assert 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')
 | |
|         assert not serializer.is_valid()
 | |
|         assert serializer.errors == {
 | |
|             'non_field_errors': [
 | |
|                 'Invalid data. Expected a dictionary, but got str.',
 | |
|             ]
 | |
|         }
 | |
| 
 | |
| 
 | |
| # 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})
 | |
|         assert serializer.is_valid()
 | |
| 
 | |
|     def test_max_value_validation_serializer_fails(self):
 | |
|         serializer = ValidationMaxValueValidatorModelSerializer(data={'number_value': 101})
 | |
|         assert not serializer.is_valid()
 | |
|         assert serializer.errors == {
 | |
|             'number_value': [
 | |
|                 'Ensure this value is less than or equal to 100.'
 | |
|             ]
 | |
|         }
 | |
| 
 | |
|     def test_max_value_validation_success(self):
 | |
|         obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
 | |
|         request = factory.patch('/{}'.format(obj.pk), {'number_value': 98}, format='json')
 | |
|         view = UpdateMaxValueValidationModel().as_view()
 | |
|         response = view(request, pk=obj.pk).render()
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
|     def test_max_value_validation_fail(self):
 | |
|         obj = ValidationMaxValueValidatorModel.objects.create(number_value=100)
 | |
|         request = factory.patch('/{}'.format(obj.pk), {'number_value': 101}, format='json')
 | |
|         view = UpdateMaxValueValidationModel().as_view()
 | |
|         response = view(request, pk=obj.pk).render()
 | |
|         assert response.content == b'{"number_value":["Ensure this value is less than or equal to 100."]}'
 | |
|         assert 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
 |