Skip to content

Commit a8595a8

Browse files
authored
Fix raising on nullable fields part of UniqueConstraint (#9531)
* Add test to reproduce problem with nullable fields part of a unique constraint Ref #9378 * Simplify test case and add similar case for unique_together * Add test for unique together in a better place * Default nullable fields to null in unique constraints checks * Remove redundant test and move other to more appropriate place
1 parent dbac145 commit a8595a8

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

rest_framework/serializers.py

+2
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,8 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs
14901490
default = timezone.now
14911491
elif unique_constraint_field.has_default():
14921492
default = unique_constraint_field.default
1493+
elif unique_constraint_field.null:
1494+
default = None
14931495
else:
14941496
default = empty
14951497

tests/test_validators.py

+32
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,14 @@ def test_ignore_validation_for_null_fields(self):
441441
serializer = NullUniquenessTogetherSerializer(data=data)
442442
assert serializer.is_valid()
443443

444+
def test_ignore_validation_for_missing_nullable_fields(self):
445+
data = {
446+
'date': datetime.date(2000, 1, 1),
447+
'race_name': 'Paris Marathon',
448+
}
449+
serializer = NullUniquenessTogetherSerializer(data=data)
450+
assert serializer.is_valid(), serializer.errors
451+
444452
def test_do_not_ignore_validation_for_null_fields(self):
445453
# None values that are not on fields part of the uniqueness constraint
446454
# do not cause the instance to skip validation.
@@ -539,12 +547,30 @@ class Meta:
539547
]
540548

541549

550+
class UniqueConstraintNullableModel(models.Model):
551+
title = models.CharField(max_length=100)
552+
age = models.IntegerField(null=True)
553+
tag = models.CharField(max_length=100, null=True)
554+
555+
class Meta:
556+
constraints = [
557+
# Unique constraint on 2 nullable fields
558+
models.UniqueConstraint(name='unique_constraint', fields=('age', 'tag'))
559+
]
560+
561+
542562
class UniqueConstraintSerializer(serializers.ModelSerializer):
543563
class Meta:
544564
model = UniqueConstraintModel
545565
fields = '__all__'
546566

547567

568+
class UniqueConstraintNullableSerializer(serializers.ModelSerializer):
569+
class Meta:
570+
model = UniqueConstraintNullableModel
571+
fields = ('title', 'age', 'tag')
572+
573+
548574
class TestUniqueConstraintValidation(TestCase):
549575
def setUp(self):
550576
self.instance = UniqueConstraintModel.objects.create(
@@ -611,6 +637,12 @@ def test_single_field_uniq_validators(self):
611637
ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators if hasattr(v, "queryset")}
612638
assert ids_in_qs == {frozenset([1]), frozenset([3])}
613639

640+
def test_nullable_unique_constraint_fields_are_not_required(self):
641+
serializer = UniqueConstraintNullableSerializer(data={'title': 'Bob'})
642+
self.assertTrue(serializer.is_valid(), serializer.errors)
643+
result = serializer.save()
644+
self.assertIsInstance(result, UniqueConstraintNullableModel)
645+
614646

615647
# Tests for `UniqueForDateValidator`
616648
# ----------------------------------

0 commit comments

Comments
 (0)