mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Refs #33342 -- Removed ExclusionConstraint.opclasses per deprecation timeline.
This commit is contained in:
		| @@ -1,5 +1,3 @@ | |||||||
| import warnings |  | ||||||
|  |  | ||||||
| from django.contrib.postgres.indexes import OpClass | from django.contrib.postgres.indexes import OpClass | ||||||
| from django.core.exceptions import ValidationError | from django.core.exceptions import ValidationError | ||||||
| from django.db import DEFAULT_DB_ALIAS, NotSupportedError | from django.db import DEFAULT_DB_ALIAS, NotSupportedError | ||||||
| @@ -9,7 +7,6 @@ from django.db.models.expressions import Exists, ExpressionList | |||||||
| from django.db.models.indexes import IndexExpression | from django.db.models.indexes import IndexExpression | ||||||
| from django.db.models.lookups import PostgresOperatorLookup | from django.db.models.lookups import PostgresOperatorLookup | ||||||
| from django.db.models.sql import Query | from django.db.models.sql import Query | ||||||
| from django.utils.deprecation import RemovedInDjango50Warning |  | ||||||
|  |  | ||||||
| __all__ = ["ExclusionConstraint"] | __all__ = ["ExclusionConstraint"] | ||||||
|  |  | ||||||
| @@ -33,7 +30,6 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|         condition=None, |         condition=None, | ||||||
|         deferrable=None, |         deferrable=None, | ||||||
|         include=None, |         include=None, | ||||||
|         opclasses=(), |  | ||||||
|         violation_error_message=None, |         violation_error_message=None, | ||||||
|     ): |     ): | ||||||
|         if index_type and index_type.lower() not in {"gist", "spgist"}: |         if index_type and index_type.lower() not in {"gist", "spgist"}: | ||||||
| @@ -57,28 +53,11 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|             ) |             ) | ||||||
|         if not isinstance(include, (type(None), list, tuple)): |         if not isinstance(include, (type(None), list, tuple)): | ||||||
|             raise ValueError("ExclusionConstraint.include must be a list or tuple.") |             raise ValueError("ExclusionConstraint.include must be a list or tuple.") | ||||||
|         if not isinstance(opclasses, (list, tuple)): |  | ||||||
|             raise ValueError("ExclusionConstraint.opclasses must be a list or tuple.") |  | ||||||
|         if opclasses and len(expressions) != len(opclasses): |  | ||||||
|             raise ValueError( |  | ||||||
|                 "ExclusionConstraint.expressions and " |  | ||||||
|                 "ExclusionConstraint.opclasses must have the same number of " |  | ||||||
|                 "elements." |  | ||||||
|             ) |  | ||||||
|         self.expressions = expressions |         self.expressions = expressions | ||||||
|         self.index_type = index_type or "GIST" |         self.index_type = index_type or "GIST" | ||||||
|         self.condition = condition |         self.condition = condition | ||||||
|         self.deferrable = deferrable |         self.deferrable = deferrable | ||||||
|         self.include = tuple(include) if include else () |         self.include = tuple(include) if include else () | ||||||
|         self.opclasses = opclasses |  | ||||||
|         if self.opclasses: |  | ||||||
|             warnings.warn( |  | ||||||
|                 "The opclasses argument is deprecated in favor of using " |  | ||||||
|                 "django.contrib.postgres.indexes.OpClass in " |  | ||||||
|                 "ExclusionConstraint.expressions.", |  | ||||||
|                 category=RemovedInDjango50Warning, |  | ||||||
|                 stacklevel=2, |  | ||||||
|             ) |  | ||||||
|         super().__init__(name=name, violation_error_message=violation_error_message) |         super().__init__(name=name, violation_error_message=violation_error_message) | ||||||
|  |  | ||||||
|     def _get_expressions(self, schema_editor, query): |     def _get_expressions(self, schema_editor, query): | ||||||
| @@ -86,10 +65,6 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|         for idx, (expression, operator) in enumerate(self.expressions): |         for idx, (expression, operator) in enumerate(self.expressions): | ||||||
|             if isinstance(expression, str): |             if isinstance(expression, str): | ||||||
|                 expression = F(expression) |                 expression = F(expression) | ||||||
|             try: |  | ||||||
|                 expression = OpClass(expression, self.opclasses[idx]) |  | ||||||
|             except IndexError: |  | ||||||
|                 pass |  | ||||||
|             expression = ExclusionConstraintExpression(expression, operator=operator) |             expression = ExclusionConstraintExpression(expression, operator=operator) | ||||||
|             expression.set_wrapper_classes(schema_editor.connection) |             expression.set_wrapper_classes(schema_editor.connection) | ||||||
|             expressions.append(expression) |             expressions.append(expression) | ||||||
| @@ -161,8 +136,6 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|             kwargs["deferrable"] = self.deferrable |             kwargs["deferrable"] = self.deferrable | ||||||
|         if self.include: |         if self.include: | ||||||
|             kwargs["include"] = self.include |             kwargs["include"] = self.include | ||||||
|         if self.opclasses: |  | ||||||
|             kwargs["opclasses"] = self.opclasses |  | ||||||
|         return path, args, kwargs |         return path, args, kwargs | ||||||
|  |  | ||||||
|     def __eq__(self, other): |     def __eq__(self, other): | ||||||
| @@ -174,13 +147,12 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|                 and self.condition == other.condition |                 and self.condition == other.condition | ||||||
|                 and self.deferrable == other.deferrable |                 and self.deferrable == other.deferrable | ||||||
|                 and self.include == other.include |                 and self.include == other.include | ||||||
|                 and self.opclasses == other.opclasses |  | ||||||
|                 and self.violation_error_message == other.violation_error_message |                 and self.violation_error_message == other.violation_error_message | ||||||
|             ) |             ) | ||||||
|         return super().__eq__(other) |         return super().__eq__(other) | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return "<%s: index_type=%s expressions=%s name=%s%s%s%s%s>" % ( |         return "<%s: index_type=%s expressions=%s name=%s%s%s%s>" % ( | ||||||
|             self.__class__.__qualname__, |             self.__class__.__qualname__, | ||||||
|             repr(self.index_type), |             repr(self.index_type), | ||||||
|             repr(self.expressions), |             repr(self.expressions), | ||||||
| @@ -188,7 +160,6 @@ class ExclusionConstraint(BaseConstraint): | |||||||
|             "" if self.condition is None else " condition=%s" % self.condition, |             "" if self.condition is None else " condition=%s" % self.condition, | ||||||
|             "" if self.deferrable is None else " deferrable=%r" % self.deferrable, |             "" if self.deferrable is None else " deferrable=%r" % self.deferrable, | ||||||
|             "" if not self.include else " include=%s" % repr(self.include), |             "" if not self.include else " include=%s" % repr(self.include), | ||||||
|             "" if not self.opclasses else " opclasses=%s" % repr(self.opclasses), |  | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS): |     def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS): | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ PostgreSQL supports additional data integrity constraints available from the | |||||||
| ``ExclusionConstraint`` | ``ExclusionConstraint`` | ||||||
| ======================= | ======================= | ||||||
|  |  | ||||||
| .. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None) | .. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, violation_error_message=None) | ||||||
|  |  | ||||||
|     Creates an exclusion constraint in the database. Internally, PostgreSQL |     Creates an exclusion constraint in the database. Internally, PostgreSQL | ||||||
|     implements exclusion constraints using indexes. The default index type is |     implements exclusion constraints using indexes. The default index type is | ||||||
| @@ -133,32 +133,6 @@ used for queries that select only included fields | |||||||
| ``include`` is supported for GiST indexes. PostgreSQL 14+ also supports | ``include`` is supported for GiST indexes. PostgreSQL 14+ also supports | ||||||
| ``include`` for SP-GiST indexes. | ``include`` for SP-GiST indexes. | ||||||
|  |  | ||||||
| ``opclasses`` |  | ||||||
| ------------- |  | ||||||
|  |  | ||||||
| .. attribute:: ExclusionConstraint.opclasses |  | ||||||
|  |  | ||||||
| The names of the `PostgreSQL operator classes |  | ||||||
| <https://www.postgresql.org/docs/current/indexes-opclass.html>`_ to use for |  | ||||||
| this constraint. If you require a custom operator class, you must provide one |  | ||||||
| for each expression in the constraint. |  | ||||||
|  |  | ||||||
| For example:: |  | ||||||
|  |  | ||||||
|     ExclusionConstraint( |  | ||||||
|         name='exclude_overlapping_opclasses', |  | ||||||
|         expressions=[('circle', RangeOperators.OVERLAPS)], |  | ||||||
|         opclasses=['circle_ops'], |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
| creates an exclusion constraint on ``circle`` using ``circle_ops``. |  | ||||||
|  |  | ||||||
| .. deprecated:: 4.1 |  | ||||||
|  |  | ||||||
|     The ``opclasses`` parameter is deprecated in favor of using |  | ||||||
|     :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` in |  | ||||||
|     :attr:`~ExclusionConstraint.expressions`. |  | ||||||
|  |  | ||||||
| ``violation_error_message`` | ``violation_error_message`` | ||||||
| --------------------------- | --------------------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -247,8 +247,8 @@ Minor features | |||||||
| * The new :attr:`.ExclusionConstraint.include` attribute allows creating | * The new :attr:`.ExclusionConstraint.include` attribute allows creating | ||||||
|   covering exclusion constraints on PostgreSQL 12+. |   covering exclusion constraints on PostgreSQL 12+. | ||||||
|  |  | ||||||
| * The new :attr:`.ExclusionConstraint.opclasses` attribute allows setting | * The new ``ExclusionConstraint.opclasses`` attribute allows setting PostgreSQL | ||||||
|   PostgreSQL operator classes. |   operator classes. | ||||||
|  |  | ||||||
| * The new :attr:`.JSONBAgg.ordering` attribute determines the ordering of the | * The new :attr:`.JSONBAgg.ordering` attribute determines the ordering of the | ||||||
|   aggregated elements. |   aggregated elements. | ||||||
|   | |||||||
| @@ -311,3 +311,6 @@ to remove usage of these features. | |||||||
|  |  | ||||||
| * The ``name`` argument of ``django.utils.functional.cached_property()`` is | * The ``name`` argument of ``django.utils.functional.cached_property()`` is | ||||||
|   removed. |   removed. | ||||||
|  |  | ||||||
|  | * The ``opclasses`` argument of | ||||||
|  |   ``django.contrib.postgres.constraints.ExclusionConstraint`` is removed. | ||||||
|   | |||||||
| @@ -16,10 +16,9 @@ from django.db.models import ( | |||||||
| ) | ) | ||||||
| from django.db.models.fields.json import KeyTextTransform | from django.db.models.fields.json import KeyTextTransform | ||||||
| from django.db.models.functions import Cast, Left, Lower | from django.db.models.functions import Cast, Left, Lower | ||||||
| from django.test import ignore_warnings, skipUnlessDBFeature | from django.test import skipUnlessDBFeature | ||||||
| from django.test.utils import isolate_apps | from django.test.utils import isolate_apps | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
| from django.utils.deprecation import RemovedInDjango50Warning |  | ||||||
|  |  | ||||||
| from . import PostgreSQLTestCase | from . import PostgreSQLTestCase | ||||||
| from .models import HotelReservation, IntegerArrayModel, RangesModel, Room, Scene | from .models import HotelReservation, IntegerArrayModel, RangesModel, Room, Scene | ||||||
| @@ -328,30 +327,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): | |||||||
|                 include="invalid", |                 include="invalid", | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_invalid_opclasses_type(self): |  | ||||||
|         msg = "ExclusionConstraint.opclasses must be a list or tuple." |  | ||||||
|         with self.assertRaisesMessage(ValueError, msg): |  | ||||||
|             ExclusionConstraint( |  | ||||||
|                 name="exclude_invalid_opclasses", |  | ||||||
|                 expressions=[(F("datespan"), RangeOperators.OVERLAPS)], |  | ||||||
|                 opclasses="invalid", |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_opclasses_and_expressions_same_length(self): |  | ||||||
|         msg = ( |  | ||||||
|             "ExclusionConstraint.expressions and " |  | ||||||
|             "ExclusionConstraint.opclasses must have the same number of " |  | ||||||
|             "elements." |  | ||||||
|         ) |  | ||||||
|         with self.assertRaisesMessage(ValueError, msg): |  | ||||||
|             ExclusionConstraint( |  | ||||||
|                 name="exclude_invalid_expressions_opclasses_length", |  | ||||||
|                 expressions=[(F("datespan"), RangeOperators.OVERLAPS)], |  | ||||||
|                 opclasses=["foo", "bar"], |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     def test_repr(self): |     def test_repr(self): | ||||||
|         constraint = ExclusionConstraint( |         constraint = ExclusionConstraint( | ||||||
|             name="exclude_overlapping", |             name="exclude_overlapping", | ||||||
| @@ -466,27 +441,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): | |||||||
|             ], |             ], | ||||||
|             include=["cancelled"], |             include=["cancelled"], | ||||||
|         ) |         ) | ||||||
|         with ignore_warnings(category=RemovedInDjango50Warning): |  | ||||||
|             constraint_8 = ExclusionConstraint( |  | ||||||
|                 name="exclude_overlapping", |  | ||||||
|                 expressions=[ |  | ||||||
|                     ("datespan", RangeOperators.OVERLAPS), |  | ||||||
|                     ("room", RangeOperators.EQUAL), |  | ||||||
|                 ], |  | ||||||
|                 include=["cancelled"], |  | ||||||
|                 opclasses=["range_ops", "range_ops"], |  | ||||||
|             ) |  | ||||||
|             constraint_9 = ExclusionConstraint( |  | ||||||
|                 name="exclude_overlapping", |  | ||||||
|                 expressions=[ |  | ||||||
|                     ("datespan", RangeOperators.OVERLAPS), |  | ||||||
|                     ("room", RangeOperators.EQUAL), |  | ||||||
|                 ], |  | ||||||
|                 opclasses=["range_ops", "range_ops"], |  | ||||||
|             ) |  | ||||||
|             self.assertNotEqual(constraint_2, constraint_9) |  | ||||||
|             self.assertNotEqual(constraint_7, constraint_8) |  | ||||||
|  |  | ||||||
|         constraint_10 = ExclusionConstraint( |         constraint_10 = ExclusionConstraint( | ||||||
|             name="exclude_overlapping", |             name="exclude_overlapping", | ||||||
|             expressions=[ |             expressions=[ | ||||||
| @@ -636,27 +590,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): | |||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_deconstruct_opclasses(self): |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name="exclude_overlapping", |  | ||||||
|             expressions=[("datespan", RangeOperators.OVERLAPS)], |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|         ) |  | ||||||
|         path, args, kwargs = constraint.deconstruct() |  | ||||||
|         self.assertEqual( |  | ||||||
|             path, "django.contrib.postgres.constraints.ExclusionConstraint" |  | ||||||
|         ) |  | ||||||
|         self.assertEqual(args, ()) |  | ||||||
|         self.assertEqual( |  | ||||||
|             kwargs, |  | ||||||
|             { |  | ||||||
|                 "name": "exclude_overlapping", |  | ||||||
|                 "expressions": [("datespan", RangeOperators.OVERLAPS)], |  | ||||||
|                 "opclasses": ["range_ops"], |  | ||||||
|             }, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     def _test_range_overlaps(self, constraint): |     def _test_range_overlaps(self, constraint): | ||||||
|         # Create exclusion constraint. |         # Create exclusion constraint. | ||||||
|         self.assertNotIn( |         self.assertNotIn( | ||||||
| @@ -759,23 +692,6 @@ class ExclusionConstraintTests(PostgreSQLTestCase): | |||||||
|             exclude={"datespan", "start", "end", "room"}, |             exclude={"datespan", "start", "end", "room"}, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_range_overlaps_custom_opclasses(self): |  | ||||||
|         class TsTzRange(Func): |  | ||||||
|             function = "TSTZRANGE" |  | ||||||
|             output_field = DateTimeRangeField() |  | ||||||
|  |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name="exclude_overlapping_reservations_custom", |  | ||||||
|             expressions=[ |  | ||||||
|                 (TsTzRange("start", "end", RangeBoundary()), RangeOperators.OVERLAPS), |  | ||||||
|                 ("room", RangeOperators.EQUAL), |  | ||||||
|             ], |  | ||||||
|             condition=Q(cancelled=False), |  | ||||||
|             opclasses=["range_ops", "gist_int4_ops"], |  | ||||||
|         ) |  | ||||||
|         self._test_range_overlaps(constraint) |  | ||||||
|  |  | ||||||
|     def test_range_overlaps_custom(self): |     def test_range_overlaps_custom(self): | ||||||
|         class TsTzRange(Func): |         class TsTzRange(Func): | ||||||
|             function = "TSTZRANGE" |             function = "TSTZRANGE" | ||||||
| @@ -1203,137 +1119,3 @@ class ExclusionConstraintTests(PostgreSQLTestCase): | |||||||
|             constraint_name, |             constraint_name, | ||||||
|             self.get_constraints(ModelWithExclusionConstraint._meta.db_table), |             self.get_constraints(ModelWithExclusionConstraint._meta.db_table), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExclusionConstraintOpclassesDepracationTests(PostgreSQLTestCase): |  | ||||||
|     def get_constraints(self, table): |  | ||||||
|         """Get the constraints on the table using a new cursor.""" |  | ||||||
|         with connection.cursor() as cursor: |  | ||||||
|             return connection.introspection.get_constraints(cursor, table) |  | ||||||
|  |  | ||||||
|     def test_warning(self): |  | ||||||
|         msg = ( |  | ||||||
|             "The opclasses argument is deprecated in favor of using " |  | ||||||
|             "django.contrib.postgres.indexes.OpClass in " |  | ||||||
|             "ExclusionConstraint.expressions." |  | ||||||
|         ) |  | ||||||
|         with self.assertWarnsMessage(RemovedInDjango50Warning, msg): |  | ||||||
|             ExclusionConstraint( |  | ||||||
|                 name="exclude_overlapping", |  | ||||||
|                 expressions=[(F("datespan"), RangeOperators.ADJACENT_TO)], |  | ||||||
|                 opclasses=["range_ops"], |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_repr(self): |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name="exclude_overlapping", |  | ||||||
|             expressions=[(F("datespan"), RangeOperators.ADJACENT_TO)], |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|         ) |  | ||||||
|         self.assertEqual( |  | ||||||
|             repr(constraint), |  | ||||||
|             "<ExclusionConstraint: index_type='GIST' expressions=[" |  | ||||||
|             "(F(datespan), '-|-')] name='exclude_overlapping' " |  | ||||||
|             "opclasses=['range_ops']>", |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_range_adjacent_opclasses(self): |  | ||||||
|         constraint_name = "ints_adjacent_opclasses" |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name=constraint_name, |  | ||||||
|             expressions=[("ints", RangeOperators.ADJACENT_TO)], |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|         ) |  | ||||||
|         with connection.schema_editor() as editor: |  | ||||||
|             editor.add_constraint(RangesModel, constraint) |  | ||||||
|         constraints = self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         self.assertIn(constraint_name, constraints) |  | ||||||
|         with editor.connection.cursor() as cursor: |  | ||||||
|             cursor.execute(SchemaTests.get_opclass_query, [constraint.name]) |  | ||||||
|             self.assertEqual( |  | ||||||
|                 cursor.fetchall(), |  | ||||||
|                 [("range_ops", constraint.name)], |  | ||||||
|             ) |  | ||||||
|         RangesModel.objects.create(ints=(20, 50)) |  | ||||||
|         with self.assertRaises(IntegrityError), transaction.atomic(): |  | ||||||
|             RangesModel.objects.create(ints=(10, 20)) |  | ||||||
|         RangesModel.objects.create(ints=(10, 19)) |  | ||||||
|         RangesModel.objects.create(ints=(51, 60)) |  | ||||||
|         # Drop the constraint. |  | ||||||
|         with connection.schema_editor() as editor: |  | ||||||
|             editor.remove_constraint(RangesModel, constraint) |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_range_adjacent_opclasses_condition(self): |  | ||||||
|         constraint_name = "ints_adjacent_opclasses_condition" |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name=constraint_name, |  | ||||||
|             expressions=[("ints", RangeOperators.ADJACENT_TO)], |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|             condition=Q(id__gte=100), |  | ||||||
|         ) |  | ||||||
|         with connection.schema_editor() as editor: |  | ||||||
|             editor.add_constraint(RangesModel, constraint) |  | ||||||
|         self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_range_adjacent_opclasses_deferrable(self): |  | ||||||
|         constraint_name = "ints_adjacent_opclasses_deferrable" |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name=constraint_name, |  | ||||||
|             expressions=[("ints", RangeOperators.ADJACENT_TO)], |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|             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)) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     def test_range_adjacent_gist_opclasses_include(self): |  | ||||||
|         constraint_name = "ints_adjacent_gist_opclasses_include" |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name=constraint_name, |  | ||||||
|             expressions=[("ints", RangeOperators.ADJACENT_TO)], |  | ||||||
|             index_type="gist", |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|             include=["decimals"], |  | ||||||
|         ) |  | ||||||
|         with connection.schema_editor() as editor: |  | ||||||
|             editor.add_constraint(RangesModel, constraint) |  | ||||||
|         self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) |  | ||||||
|  |  | ||||||
|     @ignore_warnings(category=RemovedInDjango50Warning) |  | ||||||
|     @skipUnlessDBFeature("supports_covering_spgist_indexes") |  | ||||||
|     def test_range_adjacent_spgist_opclasses_include(self): |  | ||||||
|         constraint_name = "ints_adjacent_spgist_opclasses_include" |  | ||||||
|         self.assertNotIn( |  | ||||||
|             constraint_name, self.get_constraints(RangesModel._meta.db_table) |  | ||||||
|         ) |  | ||||||
|         constraint = ExclusionConstraint( |  | ||||||
|             name=constraint_name, |  | ||||||
|             expressions=[("ints", RangeOperators.ADJACENT_TO)], |  | ||||||
|             index_type="spgist", |  | ||||||
|             opclasses=["range_ops"], |  | ||||||
|             include=["decimals"], |  | ||||||
|         ) |  | ||||||
|         with connection.schema_editor() as editor: |  | ||||||
|             editor.add_constraint(RangesModel, constraint) |  | ||||||
|         self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user