diff options
| author | David Sanders <shang.xiao.sanders@gmail.com> | 2024-08-05 08:22:29 +0200 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2024-08-05 17:42:19 +0200 |
| commit | 333cfab512689490ea8a6a95598a94d77816fa2d (patch) | |
| tree | 2783f13b4fb53f4c2b88f62a4a09715ceaa5d98e /tests | |
| parent | e88ef6a27d125da95e530be20b879fb4f540f7f6 (diff) | |
[5.0.x] Fixed #35638 -- Updated validate_constraints to consider db_default.
Backport of 509763c79952cde02d9f5b584af4278bdbed77b2 from main.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/constraints/models.py | 7 | ||||
| -rw-r--r-- | tests/constraints/tests.py | 71 | ||||
| -rw-r--r-- | tests/postgres_tests/migrations/0002_create_test_models.py | 2 | ||||
| -rw-r--r-- | tests/postgres_tests/models.py | 2 | ||||
| -rw-r--r-- | tests/postgres_tests/test_constraints.py | 9 | ||||
| -rw-r--r-- | tests/validation/models.py | 2 | ||||
| -rw-r--r-- | tests/validation/test_unique.py | 14 |
7 files changed, 103 insertions, 4 deletions
diff --git a/tests/constraints/models.py b/tests/constraints/models.py index 3ea5cf2323..c336e110da 100644 --- a/tests/constraints/models.py +++ b/tests/constraints/models.py @@ -128,3 +128,10 @@ class JSONFieldModel(models.Model): class Meta: required_db_features = {"supports_json_field"} + + +class ModelWithDatabaseDefault(models.Model): + field = models.CharField(max_length=255) + field_with_db_default = models.CharField( + max_length=255, db_default=models.Value("field_with_db_default") + ) diff --git a/tests/constraints/tests.py b/tests/constraints/tests.py index 67d00b67ce..2df3706995 100644 --- a/tests/constraints/tests.py +++ b/tests/constraints/tests.py @@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError from django.db import IntegrityError, connection, models from django.db.models import F from django.db.models.constraints import BaseConstraint, UniqueConstraint -from django.db.models.functions import Abs, Lower +from django.db.models.functions import Abs, Lower, Upper from django.db.transaction import atomic from django.test import SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test.utils import ignore_warnings @@ -14,6 +14,7 @@ from .models import ( ChildModel, ChildUniqueConstraintProduct, JSONFieldModel, + ModelWithDatabaseDefault, Product, UniqueConstraintConditionProduct, UniqueConstraintDeferrable, @@ -365,6 +366,47 @@ class CheckConstraintTests(TestCase): constraint_with_pk.validate(ChildModel, ChildModel(id=1, age=1)) constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=1), exclude={"pk"}) + @skipUnlessDBFeature("supports_json_field") + def test_validate_jsonfield_exact(self): + data = {"release": "5.0.2", "version": "stable"} + json_exact_constraint = models.CheckConstraint( + check=models.Q(data__version="stable"), + name="only_stable_version", + ) + json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data)) + + data = {"release": "5.0.2", "version": "not stable"} + msg = f"Constraint “{json_exact_constraint.name}” is violated." + with self.assertRaisesMessage(ValidationError, msg): + json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data)) + + def test_database_default(self): + models.CheckConstraint( + check=models.Q(field_with_db_default="field_with_db_default"), + name="check_field_with_db_default", + ).validate(ModelWithDatabaseDefault, ModelWithDatabaseDefault()) + + # Ensure that a check also does not silently pass with either + # FieldError or DatabaseError when checking with a db_default. + with self.assertRaises(ValidationError): + models.CheckConstraint( + check=models.Q( + field_with_db_default="field_with_db_default", field="field" + ), + name="check_field_with_db_default_2", + ).validate( + ModelWithDatabaseDefault, ModelWithDatabaseDefault(field="not-field") + ) + + with self.assertRaises(ValidationError): + models.CheckConstraint( + check=models.Q(field_with_db_default="field_with_db_default"), + name="check_field_with_db_default", + ).validate( + ModelWithDatabaseDefault, + ModelWithDatabaseDefault(field_with_db_default="other value"), + ) + class UniqueConstraintTests(TestCase): @classmethod @@ -1220,3 +1262,30 @@ class UniqueConstraintTests(TestCase): msg = "A unique constraint must be named." with self.assertRaisesMessage(ValueError, msg): models.UniqueConstraint(fields=["field"]) + + def test_database_default(self): + models.UniqueConstraint( + fields=["field_with_db_default"], name="unique_field_with_db_default" + ).validate(ModelWithDatabaseDefault, ModelWithDatabaseDefault()) + models.UniqueConstraint( + Upper("field_with_db_default"), + name="unique_field_with_db_default_expression", + ).validate(ModelWithDatabaseDefault, ModelWithDatabaseDefault()) + + ModelWithDatabaseDefault.objects.create() + + msg = ( + "Model with database default with this Field with db default already " + "exists." + ) + with self.assertRaisesMessage(ValidationError, msg): + models.UniqueConstraint( + fields=["field_with_db_default"], name="unique_field_with_db_default" + ).validate(ModelWithDatabaseDefault, ModelWithDatabaseDefault()) + + msg = "Constraint “unique_field_with_db_default_expression” is violated." + with self.assertRaisesMessage(ValidationError, msg): + models.UniqueConstraint( + Upper("field_with_db_default"), + name="unique_field_with_db_default_expression", + ).validate(ModelWithDatabaseDefault, ModelWithDatabaseDefault()) diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py index 011a0d729b..d5cae460d2 100644 --- a/tests/postgres_tests/migrations/0002_create_test_models.py +++ b/tests/postgres_tests/migrations/0002_create_test_models.py @@ -454,7 +454,7 @@ class Migration(migrations.Migration): primary_key=True, ), ), - ("ints", IntegerRangeField(null=True, blank=True)), + ("ints", IntegerRangeField(null=True, blank=True, db_default=(5, 10))), ("bigints", BigIntegerRangeField(null=True, blank=True)), ("decimals", DecimalRangeField(null=True, blank=True)), ("timestamps", DateTimeRangeField(null=True, blank=True)), diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py index 05f2732fb8..0e81eea7ec 100644 --- a/tests/postgres_tests/models.py +++ b/tests/postgres_tests/models.py @@ -141,7 +141,7 @@ class LineSavedSearch(PostgreSQLModel): class RangesModel(PostgreSQLModel): - ints = IntegerRangeField(blank=True, null=True) + ints = IntegerRangeField(blank=True, null=True, db_default=(5, 10)) bigints = BigIntegerRangeField(blank=True, null=True) decimals = DecimalRangeField(blank=True, null=True) timestamps = DateTimeRangeField(blank=True, null=True) diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py index e5a8e9dbe9..928947e225 100644 --- a/tests/postgres_tests/test_constraints.py +++ b/tests/postgres_tests/test_constraints.py @@ -1169,3 +1169,12 @@ class ExclusionConstraintTests(PostgreSQLTestCase): constraint_name, self.get_constraints(ModelWithExclusionConstraint._meta.db_table), ) + + def test_database_default(self): + constraint = ExclusionConstraint( + name="ints_equal", expressions=[("ints", RangeOperators.EQUAL)] + ) + RangesModel.objects.create() + msg = "Constraint “ints_equal” is violated." + with self.assertRaisesMessage(ValidationError, msg): + constraint.validate(RangesModel, RangesModel()) diff --git a/tests/validation/models.py b/tests/validation/models.py index 612a8dd63a..a54d72fb0e 100644 --- a/tests/validation/models.py +++ b/tests/validation/models.py @@ -48,7 +48,7 @@ class ModelToValidate(models.Model): class UniqueFieldsModel(models.Model): unique_charfield = models.CharField(max_length=100, unique=True) - unique_integerfield = models.IntegerField(unique=True) + unique_integerfield = models.IntegerField(unique=True, db_default=42) non_unique_field = models.IntegerField() diff --git a/tests/validation/test_unique.py b/tests/validation/test_unique.py index 4a8b3894f0..36ee6e9da0 100644 --- a/tests/validation/test_unique.py +++ b/tests/validation/test_unique.py @@ -146,6 +146,20 @@ class PerformUniqueChecksTest(TestCase): mtv = ModelToValidate(number=10, name="Some Name") mtv.full_clean() + def test_unique_db_default(self): + UniqueFieldsModel.objects.create(unique_charfield="foo", non_unique_field=42) + um = UniqueFieldsModel(unique_charfield="bar", non_unique_field=42) + with self.assertRaises(ValidationError) as cm: + um.full_clean() + self.assertEqual( + cm.exception.message_dict, + { + "unique_integerfield": [ + "Unique fields model with this Unique integerfield already exists." + ] + }, + ) + def test_unique_for_date(self): Post.objects.create( title="Django 1.0 is released", |
