summaryrefslogtreecommitdiff
path: root/tests/validation
diff options
context:
space:
mode:
authorGagaro <gagaro42@gmail.com>2022-01-31 16:04:13 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-05-10 11:22:23 +0200
commit667105877e6723c6985399803a364848891513cc (patch)
treeb6b3a9fe9f2c8767bc6f6a68f0580eef021b2b55 /tests/validation
parent441103a04d1d167dc870eaaf90e3fba974f67c93 (diff)
downloaddjango-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.py56
-rw-r--r--tests/validation/test_constraints.py95
-rw-r--r--tests/validation/test_unique.py4
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",