1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

[5.1.x] Fixed #35638 -- Updated validate_constraints to consider db_default.

Backport of 509763c799 from main.
This commit is contained in:
David Sanders
2024-08-05 08:22:29 +02:00
committed by Sarah Boyce
parent 78654a29b8
commit aed4ffe189
10 changed files with 130 additions and 13 deletions

View File

@@ -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")
)

View File

@@ -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,
@@ -396,6 +397,33 @@ class CheckConstraintTests(TestCase):
with self.assertWarnsRegex(RemovedInDjango60Warning, msg):
self.assertIs(constraint.check, other_condition)
def test_database_default(self):
models.CheckConstraint(
condition=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(
condition=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(
condition=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
@@ -1251,3 +1279,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())

View File

@@ -434,7 +434,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)),

View File

@@ -130,7 +130,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)

View File

@@ -1238,3 +1238,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())

View File

@@ -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()

View File

@@ -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",