Merge pull request #5028 from Ekluv/ekluv_5004

fix unique=True validation for ChoiceField
This commit is contained in:
José Padilla 2017-03-28 13:57:52 -07:00 committed by GitHub
commit a652ebd292
2 changed files with 84 additions and 66 deletions

View File

@ -123,43 +123,8 @@ def get_field_kwargs(field_name, model_field):
kwargs['allow_folders'] = model_field.allow_folders kwargs['allow_folders'] = model_field.allow_folders
if model_field.choices: if model_field.choices:
# If this model field contains choices, then return early.
# Further keyword arguments are not valid.
kwargs['choices'] = model_field.choices kwargs['choices'] = model_field.choices
return kwargs else:
# 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.
max_length = getattr(model_field, 'max_length', None)
if max_length is not None and (isinstance(model_field, models.CharField) or
isinstance(model_field, models.TextField)):
kwargs['max_length'] = max_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxLengthValidator)
]
# Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator.
min_length = next((
validator.limit_value for validator in validator_kwarg
if isinstance(validator, validators.MinLengthValidator)
), None)
if min_length is not None and isinstance(model_field, models.CharField):
kwargs['min_length'] = min_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MinLengthValidator)
]
# Ensure that max_value is passed explicitly as a keyword arg, # Ensure that max_value is passed explicitly as a keyword arg,
# rather than as a validator. # rather than as a validator.
max_value = next(( max_value = next((
@ -215,6 +180,37 @@ def get_field_kwargs(field_name, model_field):
validator for validator in validator_kwarg validator for validator in validator_kwarg
if validator is not validators.validate_ipv46_address 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.
max_length = getattr(model_field, 'max_length', None)
if max_length is not None and (isinstance(model_field, models.CharField) or
isinstance(model_field, models.TextField)):
kwargs['max_length'] = max_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxLengthValidator)
]
# Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator.
min_length = next((
validator.limit_value for validator in validator_kwarg
if isinstance(validator, validators.MinLengthValidator)
), None)
if min_length is not None and isinstance(model_field, models.CharField):
kwargs['min_length'] = min_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MinLengthValidator)
]
if getattr(model_field, 'unique', False): if getattr(model_field, 'unique', False):
unique_error_message = model_field.error_messages.get('unique', None) unique_error_message = model_field.error_messages.get('unique', None)

View File

@ -99,6 +99,15 @@ class Issue3674ChildModel(models.Model):
value = models.CharField(primary_key=True, max_length=64) 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): class TestModelSerializer(TestCase):
def test_create_method(self): def test_create_method(self):
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
@ -1080,3 +1089,16 @@ class Issue4897TestCase(TestCase):
with pytest.raises(AssertionError) as cm: with pytest.raises(AssertionError) as cm:
TestSerializer(obj).fields TestSerializer(obj).fields
cm.match(r'readonly_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'})
assert not serializer.is_valid()
assert serializer.errors == {'name': ['unique choice model with this name already exists.']}