From 78f2cf86598c55a0606a49f703ffe4b951c27197 Mon Sep 17 00:00:00 2001 From: Andrea Zanotto Date: Wed, 2 Apr 2025 10:49:27 +0200 Subject: [PATCH 1/3] TestUniqueConstraintValidation failing on foreign key field. --- tests/test_validators.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/test_validators.py b/tests/test_validators.py index 29b097ef39..b0d71b91ff 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -517,11 +517,15 @@ def filter(self, **kwargs): assert queryset.called_with == {'race_name': 'bar', 'position': 1} +class FancyConditionModel(models.Model): + id = models.IntegerField(primary_key=True) + + class UniqueConstraintModel(models.Model): race_name = models.CharField(max_length=100) position = models.IntegerField() global_id = models.IntegerField() - fancy_conditions = models.IntegerField() + fancy_conditions = models.ForeignKey(FancyConditionModel, on_delete=models.CASCADE) class Meta: constraints = [ @@ -578,23 +582,24 @@ class Meta: class TestUniqueConstraintValidation(TestCase): def setUp(self): + fancy_model_condition = FancyConditionModel.objects.create(id=1) self.instance = UniqueConstraintModel.objects.create( race_name='example', position=1, global_id=1, - fancy_conditions=1 + fancy_conditions=fancy_model_condition ) UniqueConstraintModel.objects.create( race_name='example', position=2, global_id=2, - fancy_conditions=1 + fancy_conditions=fancy_model_condition ) UniqueConstraintModel.objects.create( race_name='other', position=1, global_id=3, - fancy_conditions=1 + fancy_conditions=fancy_model_condition ) def test_repr(self): @@ -618,24 +623,27 @@ def test_unique_together_condition(self): Fields used in UniqueConstraint's condition must be included into queryset existence check """ + fancy_model_condition_9 = FancyConditionModel.objects.create(id=9) + fancy_model_condition_10 = FancyConditionModel.objects.create(id=10) + fancy_model_condition_11 = FancyConditionModel.objects.create(id=11) UniqueConstraintModel.objects.create( race_name='condition', position=1, global_id=10, - fancy_conditions=10, + fancy_conditions=fancy_model_condition_10, ) serializer = UniqueConstraintSerializer(data={ 'race_name': 'condition', 'position': 1, 'global_id': 11, - 'fancy_conditions': 9, + 'fancy_conditions': fancy_model_condition_9, }) assert serializer.is_valid() serializer = UniqueConstraintSerializer(data={ 'race_name': 'condition', 'position': 1, 'global_id': 11, - 'fancy_conditions': 11, + 'fancy_conditions': fancy_model_condition_11, }) assert not serializer.is_valid() From 68af9408b7ca4dc64444c940ea99a1749c6a8a0a Mon Sep 17 00:00:00 2001 From: Andrea Zanotto Date: Tue, 8 Apr 2025 09:45:45 +0200 Subject: [PATCH 2/3] Revert "TestUniqueConstraintValidation failing on foreign key field." This reverts commit 78f2cf86598c55a0606a49f703ffe4b951c27197. --- tests/test_validators.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/test_validators.py b/tests/test_validators.py index b0d71b91ff..29b097ef39 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -517,15 +517,11 @@ def filter(self, **kwargs): assert queryset.called_with == {'race_name': 'bar', 'position': 1} -class FancyConditionModel(models.Model): - id = models.IntegerField(primary_key=True) - - class UniqueConstraintModel(models.Model): race_name = models.CharField(max_length=100) position = models.IntegerField() global_id = models.IntegerField() - fancy_conditions = models.ForeignKey(FancyConditionModel, on_delete=models.CASCADE) + fancy_conditions = models.IntegerField() class Meta: constraints = [ @@ -582,24 +578,23 @@ class Meta: class TestUniqueConstraintValidation(TestCase): def setUp(self): - fancy_model_condition = FancyConditionModel.objects.create(id=1) self.instance = UniqueConstraintModel.objects.create( race_name='example', position=1, global_id=1, - fancy_conditions=fancy_model_condition + fancy_conditions=1 ) UniqueConstraintModel.objects.create( race_name='example', position=2, global_id=2, - fancy_conditions=fancy_model_condition + fancy_conditions=1 ) UniqueConstraintModel.objects.create( race_name='other', position=1, global_id=3, - fancy_conditions=fancy_model_condition + fancy_conditions=1 ) def test_repr(self): @@ -623,27 +618,24 @@ def test_unique_together_condition(self): Fields used in UniqueConstraint's condition must be included into queryset existence check """ - fancy_model_condition_9 = FancyConditionModel.objects.create(id=9) - fancy_model_condition_10 = FancyConditionModel.objects.create(id=10) - fancy_model_condition_11 = FancyConditionModel.objects.create(id=11) UniqueConstraintModel.objects.create( race_name='condition', position=1, global_id=10, - fancy_conditions=fancy_model_condition_10, + fancy_conditions=10, ) serializer = UniqueConstraintSerializer(data={ 'race_name': 'condition', 'position': 1, 'global_id': 11, - 'fancy_conditions': fancy_model_condition_9, + 'fancy_conditions': 9, }) assert serializer.is_valid() serializer = UniqueConstraintSerializer(data={ 'race_name': 'condition', 'position': 1, 'global_id': 11, - 'fancy_conditions': fancy_model_condition_11, + 'fancy_conditions': 11, }) assert not serializer.is_valid() From 4920fd5338bdc8f2c7904d8d78a819a6282bf3c3 Mon Sep 17 00:00:00 2001 From: Andrea Zanotto Date: Tue, 8 Apr 2025 09:49:19 +0200 Subject: [PATCH 3/3] Add TestUniqueConstraintForeignKeyValidation test failing on foreign key field. --- tests/test_validators.py | 157 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/tests/test_validators.py b/tests/test_validators.py index 29b097ef39..e40ed5dc82 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -552,6 +552,45 @@ class Meta: ] +class FancyConditionModel(models.Model): + id = models.IntegerField(primary_key=True) + + +class UniqueConstraintForeignKeyModel(models.Model): + race_name = models.CharField(max_length=100) + position = models.IntegerField() + global_id = models.IntegerField() + fancy_conditions = models.ForeignKey(FancyConditionModel, on_delete=models.CASCADE) + + class Meta: + constraints = [ + models.UniqueConstraint( + name="unique_constraint_foreign_key_model_global_id_uniq", + fields=('global_id',), + ), + models.UniqueConstraint( + name="unique_constraint_foreign_key_model_fancy_1_uniq", + fields=('fancy_conditions',), + condition=models.Q(global_id__lte=1) + ), + models.UniqueConstraint( + name="unique_constraint_foreign_key_model_fancy_3_uniq", + fields=('fancy_conditions',), + condition=models.Q(global_id__gte=3) + ), + models.UniqueConstraint( + name="unique_constraint_foreign_key_model_together_uniq", + fields=('race_name', 'position'), + condition=models.Q(race_name='example'), + ), + models.UniqueConstraint( + name='unique_constraint_foreign_key_model_together_uniq2', + fields=('race_name', 'position'), + condition=models.Q(fancy_conditions__gte=10), + ), + ] + + class UniqueConstraintNullableModel(models.Model): title = models.CharField(max_length=100) age = models.IntegerField(null=True) @@ -570,6 +609,12 @@ class Meta: fields = '__all__' +class UniqueConstraintForeignKeySerializer(serializers.ModelSerializer): + class Meta: + model = UniqueConstraintForeignKeyModel + fields = '__all__' + + class UniqueConstraintNullableSerializer(serializers.ModelSerializer): class Meta: model = UniqueConstraintNullableModel @@ -684,6 +729,118 @@ def test_nullable_unique_constraint_fields_are_not_required(self): self.assertIsInstance(result, UniqueConstraintNullableModel) +class TestUniqueConstraintForeignKeyValidation(TestCase): + def setUp(self): + fancy_model_condition = FancyConditionModel.objects.create(id=1) + self.instance = UniqueConstraintForeignKeyModel.objects.create( + race_name='example', + position=1, + global_id=1, + fancy_conditions=fancy_model_condition + ) + UniqueConstraintForeignKeyModel.objects.create( + race_name='example', + position=2, + global_id=2, + fancy_conditions=fancy_model_condition + ) + UniqueConstraintForeignKeyModel.objects.create( + race_name='other', + position=1, + global_id=3, + fancy_conditions=fancy_model_condition + ) + + def test_repr(self): + serializer = UniqueConstraintForeignKeySerializer() + # the order of validators isn't deterministic so delete + # fancy_conditions field that has two of them + del serializer.fields['fancy_conditions'] + expected = dedent(r""" + UniqueConstraintForeignKeySerializer\(\): + id = IntegerField\(label='ID', read_only=True\) + race_name = CharField\(max_length=100, required=True\) + position = IntegerField\(.*required=True\) + global_id = IntegerField\(.*validators=\[\]\) + class Meta: + validators = \[\)>\] + """) + assert re.search(expected, repr(serializer)) is not None + + def test_unique_together_condition(self): + """ + Fields used in UniqueConstraint's condition must be included + into queryset existence check + """ + fancy_model_condition_9 = FancyConditionModel.objects.create(id=9) + fancy_model_condition_10 = FancyConditionModel.objects.create(id=10) + fancy_model_condition_11 = FancyConditionModel.objects.create(id=11) + UniqueConstraintForeignKeyModel.objects.create( + race_name='condition', + position=1, + global_id=10, + fancy_conditions=fancy_model_condition_10, + ) + serializer = UniqueConstraintForeignKeySerializer(data={ + 'race_name': 'condition', + 'position': 1, + 'global_id': 11, + 'fancy_conditions': fancy_model_condition_9, + }) + assert serializer.is_valid() + serializer = UniqueConstraintForeignKeySerializer(data={ + 'race_name': 'condition', + 'position': 1, + 'global_id': 11, + 'fancy_conditions': fancy_model_condition_11, + }) + assert not serializer.is_valid() + + def test_unique_together_condition_fields_required(self): + """ + Fields used in UniqueConstraint's condition must be present in serializer + """ + serializer = UniqueConstraintForeignKeySerializer(data={ + 'race_name': 'condition', + 'position': 1, + 'global_id': 11, + }) + assert not serializer.is_valid() + assert serializer.errors == {'fancy_conditions': ['This field is required.']} + + class NoFieldsSerializer(serializers.ModelSerializer): + class Meta: + model = UniqueConstraintForeignKeyModel + fields = ('race_name', 'position', 'global_id') + + serializer = NoFieldsSerializer() + assert len(serializer.validators) == 1 + + def test_single_field_uniq_validators(self): + """ + UniqueConstraint with single field must be transformed into + field's UniqueValidator + """ + # Django 5 includes Max and Min values validators for IntegerField + extra_validators_qty = 2 if django_version[0] >= 5 else 0 + serializer = UniqueConstraintForeignKeySerializer() + assert len(serializer.validators) == 2 + validators = serializer.fields['global_id'].validators + assert len(validators) == 1 + extra_validators_qty + assert validators[0].queryset == UniqueConstraintForeignKeyModel.objects + + validators = serializer.fields['fancy_conditions'].validators + assert len(validators) == 2 + extra_validators_qty + 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])} + + 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` # ---------------------------------- 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