mirror of https://github.com/django/django.git
Fixed #34149 -- Allowed adding deferrable conditional exclusion constraints on PostgreSQL.
This commit is contained in:
parent
0931d5b087
commit
d6cbf39a1b
|
@ -51,8 +51,6 @@ class ExclusionConstraint(BaseConstraint):
|
|||
raise ValueError("The expressions must be a list of 2-tuples.")
|
||||
if not isinstance(condition, (type(None), Q)):
|
||||
raise ValueError("ExclusionConstraint.condition must be a Q instance.")
|
||||
if condition and deferrable:
|
||||
raise ValueError("ExclusionConstraint with conditions cannot be deferred.")
|
||||
if not isinstance(deferrable, (type(None), Deferrable)):
|
||||
raise ValueError(
|
||||
"ExclusionConstraint.deferrable must be a Deferrable instance."
|
||||
|
|
|
@ -312,16 +312,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
|
|||
deferrable="invalid",
|
||||
)
|
||||
|
||||
def test_deferrable_with_condition(self):
|
||||
msg = "ExclusionConstraint with conditions cannot be deferred."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
ExclusionConstraint(
|
||||
name="exclude_invalid_condition",
|
||||
expressions=[(F("datespan"), RangeOperators.OVERLAPS)],
|
||||
condition=Q(cancelled=False),
|
||||
deferrable=Deferrable.DEFERRED,
|
||||
)
|
||||
|
||||
def test_invalid_include_type(self):
|
||||
msg = "ExclusionConstraint.include must be a list or tuple."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
|
@ -912,6 +902,39 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
|
|||
RangesModel.objects.create(ints=(10, 19))
|
||||
RangesModel.objects.create(ints=(51, 60))
|
||||
|
||||
def test_range_adjacent_initially_deferred_with_condition(self):
|
||||
constraint_name = "ints_adjacent_deferred_with_condition"
|
||||
self.assertNotIn(
|
||||
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
||||
)
|
||||
constraint = ExclusionConstraint(
|
||||
name=constraint_name,
|
||||
expressions=[("ints", RangeOperators.ADJACENT_TO)],
|
||||
condition=Q(ints__lt=(100, 200)),
|
||||
deferrable=Deferrable.DEFERRED,
|
||||
)
|
||||
with connection.schema_editor() as editor:
|
||||
editor.add_constraint(RangesModel, constraint)
|
||||
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
|
||||
RangesModel.objects.create(ints=(20, 50))
|
||||
adjacent_range = RangesModel.objects.create(ints=(10, 20))
|
||||
# Constraint behavior can be changed with SET CONSTRAINTS.
|
||||
with self.assertRaises(IntegrityError):
|
||||
with transaction.atomic(), connection.cursor() as cursor:
|
||||
quoted_name = connection.ops.quote_name(constraint_name)
|
||||
cursor.execute(f"SET CONSTRAINTS {quoted_name} IMMEDIATE")
|
||||
# Remove adjacent range before the end of transaction.
|
||||
adjacent_range.delete()
|
||||
RangesModel.objects.create(ints=(10, 19))
|
||||
RangesModel.objects.create(ints=(51, 60))
|
||||
# Add adjacent range that doesn't match the condition.
|
||||
RangesModel.objects.create(ints=(200, 500))
|
||||
adjacent_range = RangesModel.objects.create(ints=(100, 200))
|
||||
# Constraint behavior can be changed with SET CONSTRAINTS.
|
||||
with transaction.atomic(), connection.cursor() as cursor:
|
||||
quoted_name = connection.ops.quote_name(constraint_name)
|
||||
cursor.execute(f"SET CONSTRAINTS {quoted_name} IMMEDIATE")
|
||||
|
||||
def test_range_adjacent_gist_include(self):
|
||||
constraint_name = "ints_adjacent_gist_include"
|
||||
self.assertNotIn(
|
||||
|
|
Loading…
Reference in New Issue