diff options
author | Gagaro <gagaro42@gmail.com> | 2022-01-31 16:04:13 +0100 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-05-10 11:22:23 +0200 |
commit | 667105877e6723c6985399803a364848891513cc (patch) | |
tree | b6b3a9fe9f2c8767bc6f6a68f0580eef021b2b55 /tests/validation | |
parent | 441103a04d1d167dc870eaaf90e3fba974f67c93 (diff) | |
download | django-667105877e6723c6985399803a364848891513cc.tar.gz |
Fixed #30581 -- Added support for Meta.constraints validation.
Thanks Simon Charette, Keryn Knight, and Mariusz Felisiak for reviews.
Diffstat (limited to 'tests/validation')
-rw-r--r-- | tests/validation/models.py | 56 | ||||
-rw-r--r-- | tests/validation/test_constraints.py | 95 | ||||
-rw-r--r-- | tests/validation/test_unique.py | 4 |
3 files changed, 151 insertions, 4 deletions
diff --git a/tests/validation/models.py b/tests/validation/models.py index 6934fa017c..8919a69310 100644 --- a/tests/validation/models.py +++ b/tests/validation/models.py @@ -161,3 +161,59 @@ class UniqueFuncConstraintModel(models.Model): constraints = [ models.UniqueConstraint(Lower("field"), name="func_lower_field_uq"), ] + + +class Product(models.Model): + price = models.IntegerField(null=True) + discounted_price = models.IntegerField(null=True) + + class Meta: + required_db_features = { + "supports_table_check_constraints", + } + constraints = [ + models.CheckConstraint( + check=models.Q(price__gt=models.F("discounted_price")), + name="price_gt_discounted_price_validation", + ), + ] + + +class ChildProduct(Product): + class Meta: + required_db_features = { + "supports_table_check_constraints", + } + + +class UniqueConstraintProduct(models.Model): + name = models.CharField(max_length=255) + color = models.CharField(max_length=32) + rank = models.IntegerField() + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["name", "color"], name="name_color_uniq_validation" + ), + models.UniqueConstraint(fields=["rank"], name="rank_uniq_validation"), + ] + + +class ChildUniqueConstraintProduct(UniqueConstraintProduct): + pass + + +class UniqueConstraintConditionProduct(models.Model): + name = models.CharField(max_length=255) + color = models.CharField(max_length=31, null=True, blank=True) + + class Meta: + required_db_features = {"supports_partial_indexes"} + constraints = [ + models.UniqueConstraint( + fields=["name"], + name="name_without_color_uniq_validation", + condition=models.Q(color__isnull=True), + ), + ] diff --git a/tests/validation/test_constraints.py b/tests/validation/test_constraints.py new file mode 100644 index 0000000000..0b1ee6518e --- /dev/null +++ b/tests/validation/test_constraints.py @@ -0,0 +1,95 @@ +from django.core.exceptions import ValidationError +from django.test import TestCase, skipUnlessDBFeature + +from .models import ( + ChildProduct, + ChildUniqueConstraintProduct, + Product, + UniqueConstraintConditionProduct, + UniqueConstraintProduct, +) + + +class PerformConstraintChecksTest(TestCase): + @skipUnlessDBFeature("supports_table_check_constraints") + def test_full_clean_with_check_constraints(self): + product = Product(price=10, discounted_price=15) + with self.assertRaises(ValidationError) as cm: + product.full_clean() + self.assertEqual( + cm.exception.message_dict, + { + "__all__": [ + "Constraint “price_gt_discounted_price_validation” is violated." + ] + }, + ) + + @skipUnlessDBFeature("supports_table_check_constraints") + def test_full_clean_with_check_constraints_on_child_model(self): + product = ChildProduct(price=10, discounted_price=15) + with self.assertRaises(ValidationError) as cm: + product.full_clean() + self.assertEqual( + cm.exception.message_dict, + { + "__all__": [ + "Constraint “price_gt_discounted_price_validation” is violated." + ] + }, + ) + + @skipUnlessDBFeature("supports_table_check_constraints") + def test_full_clean_with_check_constraints_disabled(self): + product = Product(price=10, discounted_price=15) + product.full_clean(validate_constraints=False) + + def test_full_clean_with_unique_constraints(self): + UniqueConstraintProduct.objects.create(name="product", color="yellow", rank=1) + tests = [ + UniqueConstraintProduct(name="product", color="yellow", rank=1), + # Child model. + ChildUniqueConstraintProduct(name="product", color="yellow", rank=1), + ] + for product in tests: + with self.subTest(model=product.__class__.__name__): + with self.assertRaises(ValidationError) as cm: + product.full_clean() + self.assertEqual( + cm.exception.message_dict, + { + "__all__": [ + "Unique constraint product with this Name and Color " + "already exists." + ], + "rank": [ + "Unique constraint product with this Rank already exists." + ], + }, + ) + + def test_full_clean_with_unique_constraints_disabled(self): + UniqueConstraintProduct.objects.create(name="product", color="yellow", rank=1) + product = UniqueConstraintProduct(name="product", color="yellow", rank=1) + product.full_clean(validate_constraints=False) + + @skipUnlessDBFeature("supports_partial_indexes") + def test_full_clean_with_partial_unique_constraints(self): + UniqueConstraintConditionProduct.objects.create(name="product") + product = UniqueConstraintConditionProduct(name="product") + with self.assertRaises(ValidationError) as cm: + product.full_clean() + self.assertEqual( + cm.exception.message_dict, + { + "__all__": [ + "Constraint “name_without_color_uniq_validation” is violated." + ] + }, + ) + + @skipUnlessDBFeature("supports_partial_indexes") + def test_full_clean_with_partial_unique_constraints_disabled(self): + UniqueConstraintConditionProduct.objects.create(name="product") + product = UniqueConstraintConditionProduct(name="product") + product.full_clean(validate_constraints=False) diff --git a/tests/validation/test_unique.py b/tests/validation/test_unique.py index b8fd95abcb..4a8b3894f0 100644 --- a/tests/validation/test_unique.py +++ b/tests/validation/test_unique.py @@ -146,10 +146,6 @@ class PerformUniqueChecksTest(TestCase): mtv = ModelToValidate(number=10, name="Some Name") mtv.full_clean() - def test_func_unique_check_not_performed(self): - with self.assertNumQueries(0): - UniqueFuncConstraintModel(field="some name").full_clean() - def test_unique_for_date(self): Post.objects.create( title="Django 1.0 is released", |