From 56fe0e4b3f1f89f448f36d1b81e16364b9a73d6c Mon Sep 17 00:00:00 2001 From: Ekluv Date: Tue, 28 Mar 2017 00:38:21 +0530 Subject: [PATCH 1/3] fix unique=True validation for ChoiceField --- rest_framework/utils/field_mapping.py | 130 +++++++++++++------------- tests/test_serializer.py | 26 ++++++ 2 files changed, 89 insertions(+), 67 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 29005f6b7c..b8817d9766 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -123,18 +123,70 @@ def get_field_kwargs(field_name, model_field): kwargs['allow_folders'] = model_field.allow_folders if model_field.choices: - # If this model field contains choices, then return early. - # Further keyword arguments are not valid. kwargs['choices'] = model_field.choices - return kwargs - - # Our decimal validation is handled in the field code, not validator code. - # (In Django 1.9+ this differs from previous style) - if isinstance(model_field, models.DecimalField) and DecimalValidator: - validator_kwarg = [ - validator for validator in validator_kwarg - if not isinstance(validator, DecimalValidator) - ] + else: + # Ensure that max_value is passed explicitly as a keyword arg, + # rather than as a validator. + max_value = next(( + validator.limit_value for validator in validator_kwarg + if isinstance(validator, validators.MaxValueValidator) + ), None) + if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): + kwargs['max_value'] = max_value + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MaxValueValidator) + ] + + # Ensure that max_value is passed explicitly as a keyword arg, + # rather than as a validator. + min_value = next(( + validator.limit_value for validator in validator_kwarg + if isinstance(validator, validators.MinValueValidator) + ), None) + if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): + kwargs['min_value'] = min_value + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.MinValueValidator) + ] + + # URLField does not need to include the URLValidator argument, + # as it is explicitly added in. + if isinstance(model_field, models.URLField): + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, validators.URLValidator) + ] + + # EmailField does not need to include the validate_email argument, + # as it is explicitly added in. + if isinstance(model_field, models.EmailField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_email + ] + + # SlugField do not need to include the 'validate_slug' argument, + if isinstance(model_field, models.SlugField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_slug + ] + + # IPAddressField do not need to include the 'validate_ipv46_address' argument, + if isinstance(model_field, models.GenericIPAddressField): + validator_kwarg = [ + validator for validator in validator_kwarg + if validator is not validators.validate_ipv46_address + ] + # Our decimal validation is handled in the field code, not validator code. + # (In Django 1.9+ this differs from previous style) + if isinstance(model_field, models.DecimalField) and DecimalValidator: + validator_kwarg = [ + validator for validator in validator_kwarg + if not isinstance(validator, DecimalValidator) + ] # Ensure that max_length is passed explicitly as a keyword arg, # rather than as a validator. @@ -160,62 +212,6 @@ def get_field_kwargs(field_name, model_field): if not isinstance(validator, validators.MinLengthValidator) ] - # Ensure that max_value is passed explicitly as a keyword arg, - # rather than as a validator. - max_value = next(( - validator.limit_value for validator in validator_kwarg - if isinstance(validator, validators.MaxValueValidator) - ), None) - if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): - kwargs['max_value'] = max_value - validator_kwarg = [ - validator for validator in validator_kwarg - if not isinstance(validator, validators.MaxValueValidator) - ] - - # Ensure that max_value is passed explicitly as a keyword arg, - # rather than as a validator. - min_value = next(( - validator.limit_value for validator in validator_kwarg - if isinstance(validator, validators.MinValueValidator) - ), None) - if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): - kwargs['min_value'] = min_value - validator_kwarg = [ - validator for validator in validator_kwarg - if not isinstance(validator, validators.MinValueValidator) - ] - - # URLField does not need to include the URLValidator argument, - # as it is explicitly added in. - if isinstance(model_field, models.URLField): - validator_kwarg = [ - validator for validator in validator_kwarg - if not isinstance(validator, validators.URLValidator) - ] - - # EmailField does not need to include the validate_email argument, - # as it is explicitly added in. - if isinstance(model_field, models.EmailField): - validator_kwarg = [ - validator for validator in validator_kwarg - if validator is not validators.validate_email - ] - - # SlugField do not need to include the 'validate_slug' argument, - if isinstance(model_field, models.SlugField): - validator_kwarg = [ - validator for validator in validator_kwarg - if validator is not validators.validate_slug - ] - - # IPAddressField do not need to include the 'validate_ipv46_address' argument, - if isinstance(model_field, models.GenericIPAddressField): - validator_kwarg = [ - validator for validator in validator_kwarg - if validator is not validators.validate_ipv46_address - ] - if getattr(model_field, 'unique', False): unique_error_message = model_field.error_messages.get('unique', None) if unique_error_message: diff --git a/tests/test_serializer.py b/tests/test_serializer.py index f76cec9c3e..37d22ed214 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -519,3 +519,29 @@ class Grandchild(Child): assert len(Parent().get_fields()) == 2 assert len(Child().get_fields()) == 2 assert len(Grandchild().get_fields()) == 2 + + +class Poll(models.Model): + CHOICES = ( + ('choice1', 'choice 1'), + ('choice2', 'choice 1'), + ) + + name = models.CharField( + 'name', max_length=254, unique=True, choices=CHOICES + ) + + +@pytest.mark.django_db +class Test5004UniqueChoiceField: + def test_unique_choice_field(self): + Poll.objects.create(name='choice1') + + class PollSerializer(serializers.ModelSerializer): + class Meta: + model = Poll + fields = '__all__' + + serializer = PollSerializer(data={'name': 'choice1'}) + assert not serializer.is_valid() + assert serializer.errors == {'name': ['poll with this name already exists.']} From ef8092ba2f662b3bc2197f322f393a5b31a67fcb Mon Sep 17 00:00:00 2001 From: Ekluv Date: Tue, 28 Mar 2017 11:50:55 +0530 Subject: [PATCH 2/3] update test case --- tests/test_model_serializer.py | 28 ++++++++++++++++++++++++++++ tests/test_serializer.py | 26 -------------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index cfa671125d..3633781fda 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -99,6 +99,15 @@ class Issue3674ChildModel(models.Model): value = models.CharField(primary_key=True, max_length=64) +class UniqueChoiceModel(models.Model): + CHOICES = ( + ('choice1', 'choice 1'), + ('choice2', 'choice 1'), + ) + + name = models.CharField(max_length=254, unique=True, choices=CHOICES) + + class TestModelSerializer(TestCase): def test_create_method(self): class TestSerializer(serializers.ModelSerializer): @@ -1080,3 +1089,22 @@ class Meta: with pytest.raises(AssertionError) as cm: TestSerializer(obj).fields cm.match(r'readonly_fields') + + +class Test5004UniqueChoiceField(TestCase): + def test_unique_choice_field(self): + class TestUniqueChoiceSerializer(serializers.ModelSerializer): + class Meta: + model = UniqueChoiceModel + fields = '__all__' + + UniqueChoiceModel.objects.create(name='choice1') + serializer = TestUniqueChoiceSerializer(data={'name': 'choice1'}) + expected = dedent(""" + TestUniqueChoiceSerializer(): + id = IntegerField(label='ID', read_only=True) + name = ChoiceField(choices=(('choice1', 'choice 1'), ('choice2', 'choice 1')), validators=[]) + """) + self.assertEqual(unicode_repr(TestUniqueChoiceSerializer()), expected) + assert not serializer.is_valid() + assert serializer.errors == {'name': ['unique choice model with this name already exists.']} diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 37d22ed214..f76cec9c3e 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -519,29 +519,3 @@ class Grandchild(Child): assert len(Parent().get_fields()) == 2 assert len(Child().get_fields()) == 2 assert len(Grandchild().get_fields()) == 2 - - -class Poll(models.Model): - CHOICES = ( - ('choice1', 'choice 1'), - ('choice2', 'choice 1'), - ) - - name = models.CharField( - 'name', max_length=254, unique=True, choices=CHOICES - ) - - -@pytest.mark.django_db -class Test5004UniqueChoiceField: - def test_unique_choice_field(self): - Poll.objects.create(name='choice1') - - class PollSerializer(serializers.ModelSerializer): - class Meta: - model = Poll - fields = '__all__' - - serializer = PollSerializer(data={'name': 'choice1'}) - assert not serializer.is_valid() - assert serializer.errors == {'name': ['poll with this name already exists.']} From d66304abe610e68cce482b40a2133d68cc654803 Mon Sep 17 00:00:00 2001 From: Ekluv Date: Tue, 28 Mar 2017 12:02:03 +0530 Subject: [PATCH 3/3] update test case --- tests/test_model_serializer.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 3633781fda..08fc3b42d5 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -1100,11 +1100,5 @@ class Meta: UniqueChoiceModel.objects.create(name='choice1') serializer = TestUniqueChoiceSerializer(data={'name': 'choice1'}) - expected = dedent(""" - TestUniqueChoiceSerializer(): - id = IntegerField(label='ID', read_only=True) - name = ChoiceField(choices=(('choice1', 'choice 1'), ('choice2', 'choice 1')), validators=[]) - """) - self.assertEqual(unicode_repr(TestUniqueChoiceSerializer()), expected) assert not serializer.is_valid() assert serializer.errors == {'name': ['unique choice model with this name already exists.']} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy