mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 16:07:38 +03:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									dbac145638
								
							
						
					
					
						commit
						a8595a8eae
					
				|  | @ -1490,6 +1490,8 @@ class ModelSerializer(Serializer): | ||||||
|                 default = timezone.now |                 default = timezone.now | ||||||
|             elif unique_constraint_field.has_default(): |             elif unique_constraint_field.has_default(): | ||||||
|                 default = unique_constraint_field.default |                 default = unique_constraint_field.default | ||||||
|  |             elif unique_constraint_field.null: | ||||||
|  |                 default = None | ||||||
|             else: |             else: | ||||||
|                 default = empty |                 default = empty | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -441,6 +441,14 @@ class TestUniquenessTogetherValidation(TestCase): | ||||||
|         serializer = NullUniquenessTogetherSerializer(data=data) |         serializer = NullUniquenessTogetherSerializer(data=data) | ||||||
|         assert serializer.is_valid() |         assert serializer.is_valid() | ||||||
| 
 | 
 | ||||||
|  |     def test_ignore_validation_for_missing_nullable_fields(self): | ||||||
|  |         data = { | ||||||
|  |             'date': datetime.date(2000, 1, 1), | ||||||
|  |             'race_name': 'Paris Marathon', | ||||||
|  |         } | ||||||
|  |         serializer = NullUniquenessTogetherSerializer(data=data) | ||||||
|  |         assert serializer.is_valid(), serializer.errors | ||||||
|  | 
 | ||||||
|     def test_do_not_ignore_validation_for_null_fields(self): |     def test_do_not_ignore_validation_for_null_fields(self): | ||||||
|         # None values that are not on fields part of the uniqueness constraint |         # None values that are not on fields part of the uniqueness constraint | ||||||
|         # do not cause the instance to skip validation. |         # do not cause the instance to skip validation. | ||||||
|  | @ -539,12 +547,30 @@ class UniqueConstraintModel(models.Model): | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class UniqueConstraintNullableModel(models.Model): | ||||||
|  |     title = models.CharField(max_length=100) | ||||||
|  |     age = models.IntegerField(null=True) | ||||||
|  |     tag = models.CharField(max_length=100, null=True) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         constraints = [ | ||||||
|  |             # Unique constraint on 2 nullable fields | ||||||
|  |             models.UniqueConstraint(name='unique_constraint', fields=('age', 'tag')) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class UniqueConstraintSerializer(serializers.ModelSerializer): | class UniqueConstraintSerializer(serializers.ModelSerializer): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = UniqueConstraintModel |         model = UniqueConstraintModel | ||||||
|         fields = '__all__' |         fields = '__all__' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class UniqueConstraintNullableSerializer(serializers.ModelSerializer): | ||||||
|  |     class Meta: | ||||||
|  |         model = UniqueConstraintNullableModel | ||||||
|  |         fields = ('title', 'age', 'tag') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class TestUniqueConstraintValidation(TestCase): | class TestUniqueConstraintValidation(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.instance = UniqueConstraintModel.objects.create( |         self.instance = UniqueConstraintModel.objects.create( | ||||||
|  | @ -611,6 +637,12 @@ class TestUniqueConstraintValidation(TestCase): | ||||||
|         ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators if hasattr(v, "queryset")} |         ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators if hasattr(v, "queryset")} | ||||||
|         assert ids_in_qs == {frozenset([1]), frozenset([3])} |         assert ids_in_qs == {frozenset([1]), frozenset([3])} | ||||||
| 
 | 
 | ||||||
|  |     def test_nullable_unique_constraint_fields_are_not_required(self): | ||||||
|  |         serializer = UniqueConstraintNullableSerializer(data={'title': 'Bob'}) | ||||||
|  |         self.assertTrue(serializer.is_valid(), serializer.errors) | ||||||
|  |         result = serializer.save() | ||||||
|  |         self.assertIsInstance(result, UniqueConstraintNullableModel) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Tests for `UniqueForDateValidator` | # Tests for `UniqueForDateValidator` | ||||||
| # ---------------------------------- | # ---------------------------------- | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user