From daf7d482dbaaa2604241a994c49f442fa15142c1 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Mon, 26 Feb 2024 00:14:26 -0500 Subject: [PATCH] Refs #35234 -- Deprecated CheckConstraint.check in favor of .condition. Once the deprecation period ends CheckConstraint.check() can become the documented method that performs system checks for BaseConstraint subclasses. --- django/db/models/constraints.py | 63 +++++++++--- docs/internals/deprecation.txt | 2 + docs/ref/models/constraints.txt | 22 +++-- docs/ref/models/options.txt | 2 +- docs/releases/3.1.txt | 2 +- docs/releases/5.1.txt | 3 + tests/admin_changelist/tests.py | 2 +- tests/check_framework/test_model_checks.py | 14 +-- tests/constraints/models.py | 8 +- tests/constraints/tests.py | 95 +++++++++++-------- .../gis_migrations/test_operations.py | 2 +- tests/introspection/models.py | 2 +- tests/invalid_models_tests/test_models.py | 43 +++++---- tests/migrations/test_autodetector.py | 19 ++-- tests/migrations/test_operations.py | 22 ++--- tests/migrations/test_optimizer.py | 7 +- tests/migrations/test_state.py | 4 +- tests/postgres_tests/test_constraints.py | 18 ++-- tests/postgres_tests/test_operations.py | 4 +- tests/schema/tests.py | 10 +- tests/validation/models.py | 2 +- 21 files changed, 210 insertions(+), 136 deletions(-) diff --git a/django/db/models/constraints.py b/django/db/models/constraints.py index 6c521700d2..e01a68e67b 100644 --- a/django/db/models/constraints.py +++ b/django/db/models/constraints.py @@ -134,13 +134,30 @@ class BaseConstraint: class CheckConstraint(BaseConstraint): + # RemovedInDjango60Warning: when the deprecation ends, replace with + # def __init__( + # self, *, condition, name, violation_error_code=None, violation_error_message=None + # ) def __init__( - self, *, check, name, violation_error_code=None, violation_error_message=None + self, + *, + name, + condition=None, + check=None, + violation_error_code=None, + violation_error_message=None, ): - self.check = check - if not getattr(check, "conditional", False): + if check is not None: + warnings.warn( + "CheckConstraint.check is deprecated in favor of `.condition`.", + RemovedInDjango60Warning, + stacklevel=2, + ) + condition = check + self.condition = condition + if not getattr(condition, "conditional", False): raise TypeError( - "CheckConstraint.check must be a Q instance or boolean expression." + "CheckConstraint.condition must be a Q instance or boolean expression." ) super().__init__( name=name, @@ -148,6 +165,24 @@ class CheckConstraint(BaseConstraint): violation_error_message=violation_error_message, ) + def _get_check(self): + warnings.warn( + "CheckConstraint.check is deprecated in favor of `.condition`.", + RemovedInDjango60Warning, + stacklevel=2, + ) + return self.condition + + def _set_check(self, value): + warnings.warn( + "CheckConstraint.check is deprecated in favor of `.condition`.", + RemovedInDjango60Warning, + stacklevel=2, + ) + self.condition = value + + check = property(_get_check, _set_check) + def _check(self, model, connection): errors = [] if not ( @@ -167,10 +202,10 @@ class CheckConstraint(BaseConstraint): ) else: references = set() - check = self.check - if isinstance(check, Q): - references.update(model._get_expr_references(check)) - if any(isinstance(expr, RawSQL) for expr in check.flatten()): + condition = self.condition + if isinstance(condition, Q): + references.update(model._get_expr_references(condition)) + if any(isinstance(expr, RawSQL) for expr in condition.flatten()): errors.append( checks.Warning( f"Check constraint {self.name!r} contains RawSQL() expression " @@ -185,7 +220,7 @@ class CheckConstraint(BaseConstraint): def _get_check_sql(self, model, schema_editor): query = Query(model=model, alias_cols=False) - where = query.build_where(self.check) + where = query.build_where(self.condition) compiler = query.get_compiler(connection=schema_editor.connection) sql, params = where.as_sql(compiler, schema_editor.connection) return sql % tuple(schema_editor.quote_value(p) for p in params) @@ -204,7 +239,7 @@ class CheckConstraint(BaseConstraint): def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS): against = instance._get_field_value_map(meta=model._meta, exclude=exclude) try: - if not Q(self.check).check(against, using=using): + if not Q(self.condition).check(against, using=using): raise ValidationError( self.get_violation_error_message(), code=self.violation_error_code ) @@ -212,9 +247,9 @@ class CheckConstraint(BaseConstraint): pass def __repr__(self): - return "<%s: check=%s name=%s%s%s>" % ( + return "<%s: condition=%s name=%s%s%s>" % ( self.__class__.__qualname__, - self.check, + self.condition, repr(self.name), ( "" @@ -233,7 +268,7 @@ class CheckConstraint(BaseConstraint): if isinstance(other, CheckConstraint): return ( self.name == other.name - and self.check == other.check + and self.condition == other.condition and self.violation_error_code == other.violation_error_code and self.violation_error_message == other.violation_error_message ) @@ -241,7 +276,7 @@ class CheckConstraint(BaseConstraint): def deconstruct(self): path, args, kwargs = super().deconstruct() - kwargs["check"] = self.check + kwargs["condition"] = self.condition return path, args, kwargs diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 8072075864..c0965f676b 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -77,6 +77,8 @@ details on these changes. * ``django.urls.register_converter()`` will no longer allow overriding existing converters. +* The ``check`` keyword argument of ``CheckConstraint`` will be removed. + .. _deprecation-removed-in-5.1: 5.1 diff --git a/docs/ref/models/constraints.txt b/docs/ref/models/constraints.txt index 0d6d87afbe..fa6edfd766 100644 --- a/docs/ref/models/constraints.txt +++ b/docs/ref/models/constraints.txt @@ -26,7 +26,7 @@ option. (including ``name``) each time. To work around name collisions, part of the name may contain ``'%(app_label)s'`` and ``'%(class)s'``, which are replaced, respectively, by the lowercased app label and class name of the - concrete model. For example ``CheckConstraint(check=Q(age__gte=18), + concrete model. For example ``CheckConstraint(condition=Q(age__gte=18), name='%(app_label)s_%(class)s_is_adult')``. .. admonition:: Validation of Constraints @@ -104,19 +104,19 @@ This method must be implemented by a subclass. ``CheckConstraint`` =================== -.. class:: CheckConstraint(*, check, name, violation_error_code=None, violation_error_message=None) +.. class:: CheckConstraint(*, condition, name, violation_error_code=None, violation_error_message=None) Creates a check constraint in the database. -``check`` ---------- +``condition`` +------------- -.. attribute:: CheckConstraint.check +.. attribute:: CheckConstraint.condition A :class:`Q` object or boolean :class:`~django.db.models.Expression` that -specifies the check you want the constraint to enforce. +specifies the conditional check you want the constraint to enforce. -For example, ``CheckConstraint(check=Q(age__gte=18), name='age_gte_18')`` +For example, ``CheckConstraint(condition=Q(age__gte=18), name='age_gte_18')`` ensures the age field is never less than 18. .. admonition:: Expression order @@ -127,7 +127,7 @@ ensures the age field is never less than 18. reasons. For example, use the following format if order matters:: CheckConstraint( - check=Q(age__gte=18) & Q(expensive_check=condition), + condition=Q(age__gte=18) & Q(expensive_check=condition), name="age_gte_18_and_others", ) @@ -138,7 +138,11 @@ ensures the age field is never less than 18. to behave the same as check constraints validation. For example, if ``age`` is a nullable field:: - CheckConstraint(check=Q(age__gte=18) | Q(age__isnull=True), name="age_gte_18") + CheckConstraint(condition=Q(age__gte=18) | Q(age__isnull=True), name="age_gte_18") + +.. deprecated:: 5.1 + + The ``check`` attribute is deprecated in favor of ``condition``. ``UniqueConstraint`` ==================== diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt index 909577be6c..3433d0730f 100644 --- a/docs/ref/models/options.txt +++ b/docs/ref/models/options.txt @@ -467,7 +467,7 @@ not be looking at your Django code. For example:: class Meta: constraints = [ - models.CheckConstraint(check=models.Q(age__gte=18), name="age_gte_18"), + models.CheckConstraint(condition=models.Q(age__gte=18), name="age_gte_18"), ] ``verbose_name`` diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index a872326200..704cf3e6d1 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -388,7 +388,7 @@ Models ``OneToOneField`` emulates the behavior of the SQL constraint ``ON DELETE RESTRICT``. -* :attr:`.CheckConstraint.check` now supports boolean expressions. +* ``CheckConstraint.check`` now supports boolean expressions. * The :meth:`.RelatedManager.add`, :meth:`~.RelatedManager.create`, and :meth:`~.RelatedManager.set` methods now accept callables as values in the diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt index 9a2f2fc6cc..b2377608f0 100644 --- a/docs/releases/5.1.txt +++ b/docs/releases/5.1.txt @@ -422,6 +422,9 @@ Miscellaneous * Overriding existing converters with ``django.urls.register_converter()`` is deprecated. +* The ``check`` keyword argument of ``CheckConstraint`` is deprecated in favor + of ``condition``. + Features removed in 5.1 ======================= diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 855d216a80..bf85cf038f 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -1443,7 +1443,7 @@ class ChangeListTests(TestCase): ["field_3", "related_4_id"], ) ], - models.CheckConstraint(check=models.Q(id__gt=0), name="foo"), + models.CheckConstraint(condition=models.Q(id__gt=0), name="foo"), models.UniqueConstraint( fields=["field_5"], condition=models.Q(id__gt=10), diff --git a/tests/check_framework/test_model_checks.py b/tests/check_framework/test_model_checks.py index 3075a61be8..be504f9c2d 100644 --- a/tests/check_framework/test_model_checks.py +++ b/tests/check_framework/test_model_checks.py @@ -287,8 +287,8 @@ class ConstraintNameTests(TestCase): class Model(models.Model): class Meta: constraints = [ - models.CheckConstraint(check=models.Q(id__gt=0), name="foo"), - models.CheckConstraint(check=models.Q(id__lt=100), name="foo"), + models.CheckConstraint(condition=models.Q(id__gt=0), name="foo"), + models.CheckConstraint(condition=models.Q(id__lt=100), name="foo"), ] self.assertEqual( @@ -303,7 +303,7 @@ class ConstraintNameTests(TestCase): ) def test_collision_in_different_models(self): - constraint = models.CheckConstraint(check=models.Q(id__gt=0), name="foo") + constraint = models.CheckConstraint(condition=models.Q(id__gt=0), name="foo") class Model1(models.Model): class Meta: @@ -328,7 +328,7 @@ class ConstraintNameTests(TestCase): class AbstractModel(models.Model): class Meta: constraints = [ - models.CheckConstraint(check=models.Q(id__gt=0), name="foo") + models.CheckConstraint(condition=models.Q(id__gt=0), name="foo") ] abstract = True @@ -354,7 +354,7 @@ class ConstraintNameTests(TestCase): class Meta: constraints = [ models.CheckConstraint( - check=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" + condition=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" ), ] abstract = True @@ -370,7 +370,7 @@ class ConstraintNameTests(TestCase): @modify_settings(INSTALLED_APPS={"append": "basic"}) @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_collision_across_apps(self, apps): - constraint = models.CheckConstraint(check=models.Q(id__gt=0), name="foo") + constraint = models.CheckConstraint(condition=models.Q(id__gt=0), name="foo") class Model1(models.Model): class Meta: @@ -397,7 +397,7 @@ class ConstraintNameTests(TestCase): @isolate_apps("basic", "check_framework", kwarg_name="apps") def test_no_collision_across_apps_interpolation(self, apps): constraint = models.CheckConstraint( - check=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" + condition=models.Q(id__gt=0), name="%(app_label)s_%(class)s_foo" ) class Model1(models.Model): diff --git a/tests/constraints/models.py b/tests/constraints/models.py index 3ea5cf2323..87b97b2a85 100644 --- a/tests/constraints/models.py +++ b/tests/constraints/models.py @@ -12,15 +12,15 @@ class Product(models.Model): } constraints = [ models.CheckConstraint( - check=models.Q(price__gt=models.F("discounted_price")), + condition=models.Q(price__gt=models.F("discounted_price")), name="price_gt_discounted_price", ), models.CheckConstraint( - check=models.Q(price__gt=0), + condition=models.Q(price__gt=0), name="%(app_label)s_%(class)s_price_gt_0", ), models.CheckConstraint( - check=models.Q( + condition=models.Q( models.Q(unit__isnull=True) | models.Q(unit__in=["μg/mL", "ng/mL"]) ), name="unicode_unit_list", @@ -113,7 +113,7 @@ class AbstractModel(models.Model): } constraints = [ models.CheckConstraint( - check=models.Q(age__gte=18), + condition=models.Q(age__gte=18), name="%(app_label)s_%(class)s_adult", ), ] diff --git a/tests/constraints/tests.py b/tests/constraints/tests.py index 40fdec6c40..86efaa79e7 100644 --- a/tests/constraints/tests.py +++ b/tests/constraints/tests.py @@ -123,104 +123,108 @@ class CheckConstraintTests(TestCase): check1 = models.Q(price__gt=models.F("discounted_price")) check2 = models.Q(price__lt=models.F("discounted_price")) self.assertEqual( - models.CheckConstraint(check=check1, name="price"), - models.CheckConstraint(check=check1, name="price"), + models.CheckConstraint(condition=check1, name="price"), + models.CheckConstraint(condition=check1, name="price"), ) - self.assertEqual(models.CheckConstraint(check=check1, name="price"), mock.ANY) - self.assertNotEqual( - models.CheckConstraint(check=check1, name="price"), - models.CheckConstraint(check=check1, name="price2"), + self.assertEqual( + models.CheckConstraint(condition=check1, name="price"), mock.ANY ) self.assertNotEqual( - models.CheckConstraint(check=check1, name="price"), - models.CheckConstraint(check=check2, name="price"), + models.CheckConstraint(condition=check1, name="price"), + models.CheckConstraint(condition=check1, name="price2"), ) - self.assertNotEqual(models.CheckConstraint(check=check1, name="price"), 1) self.assertNotEqual( - models.CheckConstraint(check=check1, name="price"), + models.CheckConstraint(condition=check1, name="price"), + models.CheckConstraint(condition=check2, name="price"), + ) + self.assertNotEqual(models.CheckConstraint(condition=check1, name="price"), 1) + self.assertNotEqual( + models.CheckConstraint(condition=check1, name="price"), models.CheckConstraint( - check=check1, name="price", violation_error_message="custom error" + condition=check1, name="price", violation_error_message="custom error" ), ) self.assertNotEqual( models.CheckConstraint( - check=check1, name="price", violation_error_message="custom error" + condition=check1, name="price", violation_error_message="custom error" ), models.CheckConstraint( - check=check1, name="price", violation_error_message="other custom error" + condition=check1, + name="price", + violation_error_message="other custom error", ), ) self.assertEqual( models.CheckConstraint( - check=check1, name="price", violation_error_message="custom error" + condition=check1, name="price", violation_error_message="custom error" ), models.CheckConstraint( - check=check1, name="price", violation_error_message="custom error" + condition=check1, name="price", violation_error_message="custom error" ), ) self.assertNotEqual( - models.CheckConstraint(check=check1, name="price"), + models.CheckConstraint(condition=check1, name="price"), models.CheckConstraint( - check=check1, name="price", violation_error_code="custom_code" + condition=check1, name="price", violation_error_code="custom_code" ), ) self.assertEqual( models.CheckConstraint( - check=check1, name="price", violation_error_code="custom_code" + condition=check1, name="price", violation_error_code="custom_code" ), models.CheckConstraint( - check=check1, name="price", violation_error_code="custom_code" + condition=check1, name="price", violation_error_code="custom_code" ), ) def test_repr(self): constraint = models.CheckConstraint( - check=models.Q(price__gt=models.F("discounted_price")), + condition=models.Q(price__gt=models.F("discounted_price")), name="price_gt_discounted_price", ) self.assertEqual( repr(constraint), - "", ) def test_repr_with_violation_error_message(self): constraint = models.CheckConstraint( - check=models.Q(price__lt=1), + condition=models.Q(price__lt=1), name="price_lt_one", violation_error_message="More than 1", ) self.assertEqual( repr(constraint), - "", ) def test_repr_with_violation_error_code(self): constraint = models.CheckConstraint( - check=models.Q(price__lt=1), + condition=models.Q(price__lt=1), name="price_lt_one", violation_error_code="more_than_one", ) self.assertEqual( repr(constraint), - "", ) def test_invalid_check_types(self): - msg = "CheckConstraint.check must be a Q instance or boolean expression." + msg = "CheckConstraint.condition must be a Q instance or boolean expression." with self.assertRaisesMessage(TypeError, msg): - models.CheckConstraint(check=models.F("discounted_price"), name="check") + models.CheckConstraint(condition=models.F("discounted_price"), name="check") def test_deconstruction(self): check = models.Q(price__gt=models.F("discounted_price")) name = "price_gt_discounted_price" - constraint = models.CheckConstraint(check=check, name=name) + constraint = models.CheckConstraint(condition=check, name=name) path, args, kwargs = constraint.deconstruct() self.assertEqual(path, "django.db.models.CheckConstraint") self.assertEqual(args, ()) - self.assertEqual(kwargs, {"check": check, "name": name}) + self.assertEqual(kwargs, {"condition": check, "name": name}) @skipUnlessDBFeature("supports_table_check_constraints") def test_database_constraint(self): @@ -255,7 +259,7 @@ class CheckConstraintTests(TestCase): def test_validate(self): check = models.Q(price__gt=models.F("discounted_price")) - constraint = models.CheckConstraint(check=check, name="price") + constraint = models.CheckConstraint(condition=check, name="price") # Invalid product. invalid_product = Product(price=10, discounted_price=42) with self.assertRaises(ValidationError): @@ -276,7 +280,7 @@ class CheckConstraintTests(TestCase): def test_validate_custom_error(self): check = models.Q(price__gt=models.F("discounted_price")) constraint = models.CheckConstraint( - check=check, + condition=check, name="price", violation_error_message="discount is fake", violation_error_code="fake_discount", @@ -290,7 +294,7 @@ class CheckConstraintTests(TestCase): def test_validate_boolean_expressions(self): constraint = models.CheckConstraint( - check=models.expressions.ExpressionWrapper( + condition=models.expressions.ExpressionWrapper( models.Q(price__gt=500) | models.Q(price__lt=500), output_field=models.BooleanField(), ), @@ -304,7 +308,7 @@ class CheckConstraintTests(TestCase): def test_validate_rawsql_expressions_noop(self): constraint = models.CheckConstraint( - check=models.expressions.RawSQL( + condition=models.expressions.RawSQL( "price < %s OR price > %s", (500, 500), output_field=models.BooleanField(), @@ -320,7 +324,7 @@ class CheckConstraintTests(TestCase): def test_validate_nullable_field_with_none(self): # Nullable fields should be considered valid on None values. constraint = models.CheckConstraint( - check=models.Q(price__gte=0), + condition=models.Q(price__gte=0), name="positive_price", ) constraint.validate(Product, Product()) @@ -328,7 +332,7 @@ class CheckConstraintTests(TestCase): @skipIfDBFeature("supports_comparing_boolean_expr") def test_validate_nullable_field_with_isnull(self): constraint = models.CheckConstraint( - check=models.Q(price__gte=0) | models.Q(price__isnull=True), + condition=models.Q(price__gte=0) | models.Q(price__isnull=True), name="positive_price", ) constraint.validate(Product, Product()) @@ -336,11 +340,11 @@ class CheckConstraintTests(TestCase): @skipUnlessDBFeature("supports_json_field") def test_validate_nullable_jsonfield(self): is_null_constraint = models.CheckConstraint( - check=models.Q(data__isnull=True), + condition=models.Q(data__isnull=True), name="nullable_data", ) is_not_null_constraint = models.CheckConstraint( - check=models.Q(data__isnull=False), + condition=models.Q(data__isnull=False), name="nullable_data", ) is_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None)) @@ -354,7 +358,7 @@ class CheckConstraintTests(TestCase): def test_validate_pk_field(self): constraint_with_pk = models.CheckConstraint( - check=~models.Q(pk=models.F("age")), + condition=~models.Q(pk=models.F("age")), name="pk_not_age_check", ) constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=2)) @@ -369,7 +373,7 @@ class CheckConstraintTests(TestCase): def test_validate_jsonfield_exact(self): data = {"release": "5.0.2", "version": "stable"} json_exact_constraint = models.CheckConstraint( - check=models.Q(data__version="stable"), + condition=models.Q(data__version="stable"), name="only_stable_version", ) json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data)) @@ -379,6 +383,19 @@ class CheckConstraintTests(TestCase): with self.assertRaisesMessage(ValidationError, msg): json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data)) + def test_check_deprecation(self): + msg = "CheckConstraint.check is deprecated in favor of `.condition`." + condition = models.Q(foo="bar") + with self.assertWarnsRegex(RemovedInDjango60Warning, msg): + constraint = models.CheckConstraint(name="constraint", check=condition) + with self.assertWarnsRegex(RemovedInDjango60Warning, msg): + self.assertIs(constraint.check, condition) + other_condition = models.Q(something="else") + with self.assertWarnsRegex(RemovedInDjango60Warning, msg): + constraint.check = other_condition + with self.assertWarnsRegex(RemovedInDjango60Warning, msg): + self.assertIs(constraint.check, other_condition) + class UniqueConstraintTests(TestCase): @classmethod diff --git a/tests/gis_tests/gis_migrations/test_operations.py b/tests/gis_tests/gis_migrations/test_operations.py index d2ad67945b..e81d44caf2 100644 --- a/tests/gis_tests/gis_migrations/test_operations.py +++ b/tests/gis_tests/gis_migrations/test_operations.py @@ -270,7 +270,7 @@ class OperationTests(OperationTestCase): Neighborhood = self.current_state.apps.get_model("gis", "Neighborhood") poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) constraint = models.CheckConstraint( - check=models.Q(geom=poly), + condition=models.Q(geom=poly), name="geom_within_constraint", ) Neighborhood._meta.constraints = [constraint] diff --git a/tests/introspection/models.py b/tests/introspection/models.py index d31eb0cbfa..c4a60ab182 100644 --- a/tests/introspection/models.py +++ b/tests/introspection/models.py @@ -84,7 +84,7 @@ class CheckConstraintModel(models.Model): } constraints = [ models.CheckConstraint( - name="up_votes_gte_0_check", check=models.Q(up_votes__gte=0) + name="up_votes_gte_0_check", condition=models.Q(up_votes__gte=0) ), ] diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py index 8df84b1182..a589fec807 100644 --- a/tests/invalid_models_tests/test_models.py +++ b/tests/invalid_models_tests/test_models.py @@ -1855,7 +1855,9 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.CheckConstraint(check=models.Q(age__gte=18), name="is_adult") + models.CheckConstraint( + condition=models.Q(age__gte=18), name="is_adult" + ) ] errors = Model.check(databases=self.databases) @@ -1880,7 +1882,9 @@ class ConstraintsTests(TestCase): class Meta: required_db_features = {"supports_table_check_constraints"} constraints = [ - models.CheckConstraint(check=models.Q(age__gte=18), name="is_adult") + models.CheckConstraint( + condition=models.Q(age__gte=18), name="is_adult" + ) ] self.assertEqual(Model.check(databases=self.databases), []) @@ -1892,7 +1896,7 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name", - check=models.Q(missing_field=2), + condition=models.Q(missing_field=2), ), ] @@ -1919,7 +1923,7 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.CheckConstraint(name="name", check=models.Q(parents=3)), + models.CheckConstraint(name="name", condition=models.Q(parents=3)), ] self.assertEqual( @@ -1942,7 +1946,7 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name", - check=models.Q(model__isnull=True), + condition=models.Q(model__isnull=True), ), ] @@ -1964,7 +1968,7 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.CheckConstraint(name="name", check=models.Q(m2m=2)), + models.CheckConstraint(name="name", condition=models.Q(m2m=2)), ] self.assertEqual( @@ -1992,7 +1996,7 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name", - check=models.Q(fk_1_id=2) | models.Q(fk_2=2), + condition=models.Q(fk_1_id=2) | models.Q(fk_2=2), ), ] @@ -2007,7 +2011,7 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name", - check=models.Q(pk__gt=5) & models.Q(age__gt=models.F("pk")), + condition=models.Q(pk__gt=5) & models.Q(age__gt=models.F("pk")), ), ] @@ -2023,7 +2027,7 @@ class ConstraintsTests(TestCase): class Meta: constraints = [ - models.CheckConstraint(name="name", check=models.Q(field1=1)), + models.CheckConstraint(name="name", condition=models.Q(field1=1)), ] self.assertEqual( @@ -2053,20 +2057,21 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name1", - check=models.Q( + condition=models.Q( field1__lt=models.F("parent__field1") + models.F("parent__field2") ), ), models.CheckConstraint( - name="name2", check=models.Q(name=Lower("parent__name")) + name="name2", condition=models.Q(name=Lower("parent__name")) ), models.CheckConstraint( - name="name3", check=models.Q(parent__field3=models.F("field1")) + name="name3", + condition=models.Q(parent__field3=models.F("field1")), ), models.CheckConstraint( name="name4", - check=models.Q(name=Lower("previous__name")), + condition=models.Q(name=Lower("previous__name")), ), ] @@ -2100,7 +2105,7 @@ class ConstraintsTests(TestCase): constraints = [ models.CheckConstraint( name="name", - check=models.Q( + condition=models.Q( ( models.Q(name="test") & models.Q(field1__lt=models.F("parent__field1")) @@ -2136,16 +2141,18 @@ class ConstraintsTests(TestCase): class Meta: required_db_features = {"supports_table_check_constraints"} constraints = [ - models.CheckConstraint(check=models.Q(id__gt=0), name="q_check"), models.CheckConstraint( - check=models.ExpressionWrapper( + condition=models.Q(id__gt=0), name="q_check" + ), + models.CheckConstraint( + condition=models.ExpressionWrapper( models.Q(price__gt=20), output_field=models.BooleanField(), ), name="expression_wrapper_check", ), models.CheckConstraint( - check=models.expressions.RawSQL( + condition=models.expressions.RawSQL( "id = 0", params=(), output_field=models.BooleanField(), @@ -2153,7 +2160,7 @@ class ConstraintsTests(TestCase): name="raw_sql_check", ), models.CheckConstraint( - check=models.Q( + condition=models.Q( models.ExpressionWrapper( models.Q( models.expressions.RawSQL( diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py index d36b72907d..4b532df516 100644 --- a/tests/migrations/test_autodetector.py +++ b/tests/migrations/test_autodetector.py @@ -287,7 +287,7 @@ class AutodetectorTests(BaseAutodetectorTests): { "constraints": [ models.CheckConstraint( - check=models.Q(name__contains="Bob"), name="name_contains_bob" + condition=models.Q(name__contains="Bob"), name="name_contains_bob" ) ] }, @@ -2756,14 +2756,15 @@ class AutodetectorTests(BaseAutodetectorTests): { "constraints": [ models.CheckConstraint( - check=models.Q(name__contains="Bob"), name="name_contains_bob" + condition=models.Q(name__contains="Bob"), + name="name_contains_bob", ) ] }, ) changes = self.get_changes([], [author]) constraint = models.CheckConstraint( - check=models.Q(name__contains="Bob"), name="name_contains_bob" + condition=models.Q(name__contains="Bob"), name="name_contains_bob" ) # Right number of migrations? self.assertEqual(len(changes["otherapp"]), 1) @@ -2789,7 +2790,7 @@ class AutodetectorTests(BaseAutodetectorTests): self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["AddConstraint"]) added_constraint = models.CheckConstraint( - check=models.Q(name__contains="Bob"), name="name_contains_bob" + condition=models.Q(name__contains="Bob"), name="name_contains_bob" ) self.assertOperationAttributes( changes, "testapp", 0, 0, model_name="author", constraint=added_constraint @@ -2838,7 +2839,7 @@ class AutodetectorTests(BaseAutodetectorTests): { "constraints": [ models.CheckConstraint( - check=models.Q(type__in=book_types.keys()), + condition=models.Q(type__in=book_types.keys()), name="book_type_check", ), ], @@ -2854,7 +2855,7 @@ class AutodetectorTests(BaseAutodetectorTests): { "constraints": [ models.CheckConstraint( - check=models.Q(("type__in", tuple(book_types))), + condition=models.Q(("type__in", tuple(book_types))), name="book_type_check", ), ], @@ -4168,7 +4169,7 @@ class AutodetectorTests(BaseAutodetectorTests): "order_with_respect_to": "book", "constraints": [ models.CheckConstraint( - check=models.Q(_order__gt=1), name="book_order_gt_1" + condition=models.Q(_order__gt=1), name="book_order_gt_1" ), ], }, @@ -4191,7 +4192,7 @@ class AutodetectorTests(BaseAutodetectorTests): "order_with_respect_to": "book", "constraints": [ models.CheckConstraint( - check=models.Q(_order__gt=1), name="book_order_gt_1" + condition=models.Q(_order__gt=1), name="book_order_gt_1" ) ], }, @@ -4241,7 +4242,7 @@ class AutodetectorTests(BaseAutodetectorTests): { "constraints": [ models.CheckConstraint( - check=models.Q(_order__gt=1), + condition=models.Q(_order__gt=1), name="book_order_gt_1", ), ] diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index f25bb290a5..3845381454 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -441,7 +441,7 @@ class OperationTests(OperationTestBase): def test_create_model_with_constraint(self): where = models.Q(pink__gt=2) check_constraint = models.CheckConstraint( - check=where, name="test_constraint_pony_pink_gt_2" + condition=where, name="test_constraint_pony_pink_gt_2" ) operation = migrations.CreateModel( "Pony", @@ -484,13 +484,13 @@ class OperationTests(OperationTestBase): def test_create_model_with_boolean_expression_in_check_constraint(self): app_label = "test_crmobechc" rawsql_constraint = models.CheckConstraint( - check=models.expressions.RawSQL( + condition=models.expressions.RawSQL( "price < %s", (1000,), output_field=models.BooleanField() ), name=f"{app_label}_price_lt_1000_raw", ) wrapper_constraint = models.CheckConstraint( - check=models.expressions.ExpressionWrapper( + condition=models.expressions.ExpressionWrapper( models.Q(price__gt=500) | models.Q(price__lt=500), output_field=models.BooleanField(), ), @@ -3858,7 +3858,7 @@ class OperationTests(OperationTestBase): project_state = self.set_up_test_model("test_addconstraint") gt_check = models.Q(pink__gt=2) gt_constraint = models.CheckConstraint( - check=gt_check, name="test_add_constraint_pony_pink_gt_2" + condition=gt_check, name="test_add_constraint_pony_pink_gt_2" ) gt_operation = migrations.AddConstraint("Pony", gt_constraint) self.assertEqual( @@ -3901,7 +3901,7 @@ class OperationTests(OperationTestBase): # Add another one. lt_check = models.Q(pink__lt=100) lt_constraint = models.CheckConstraint( - check=lt_check, name="test_add_constraint_pony_pink_lt_100" + condition=lt_check, name="test_add_constraint_pony_pink_lt_100" ) lt_operation = migrations.AddConstraint("Pony", lt_constraint) lt_operation.state_forwards("test_addconstraint", new_state) @@ -3981,8 +3981,8 @@ class OperationTests(OperationTestBase): ), ] for check, valid, invalid in checks: - with self.subTest(check=check, valid=valid, invalid=invalid): - constraint = models.CheckConstraint(check=check, name="constraint") + with self.subTest(condition=check, valid=valid, invalid=invalid): + constraint = models.CheckConstraint(condition=check, name="constraint") operation = migrations.AddConstraint("Author", constraint) to_state = from_state.clone() operation.state_forwards(app_label, to_state) @@ -4006,7 +4006,7 @@ class OperationTests(OperationTestBase): constraint_name = "add_constraint_or" from_state = self.set_up_test_model(app_label) check = models.Q(pink__gt=2, weight__gt=2) | models.Q(weight__lt=0) - constraint = models.CheckConstraint(check=check, name=constraint_name) + constraint = models.CheckConstraint(condition=check, name=constraint_name) operation = migrations.AddConstraint("Pony", constraint) to_state = from_state.clone() operation.state_forwards(app_label, to_state) @@ -4040,7 +4040,7 @@ class OperationTests(OperationTestBase): ] from_state = self.apply_operations(app_label, ProjectState(), operations) constraint = models.CheckConstraint( - check=models.Q(read=(100 - models.F("unread"))), + condition=models.Q(read=(100 - models.F("unread"))), name="test_addconstraint_combinable_sum_100", ) operation = migrations.AddConstraint("Book", constraint) @@ -4058,11 +4058,11 @@ class OperationTests(OperationTestBase): "test_removeconstraint", constraints=[ models.CheckConstraint( - check=models.Q(pink__gt=2), + condition=models.Q(pink__gt=2), name="test_remove_constraint_pony_pink_gt_2", ), models.CheckConstraint( - check=models.Q(pink__lt=100), + condition=models.Q(pink__lt=100), name="test_remove_constraint_pony_pink_lt_100", ), ], diff --git a/tests/migrations/test_optimizer.py b/tests/migrations/test_optimizer.py index 5c1fe3020e..2acbc7f09f 100644 --- a/tests/migrations/test_optimizer.py +++ b/tests/migrations/test_optimizer.py @@ -1208,7 +1208,7 @@ class OptimizerTests(SimpleTestCase): def test_add_remove_constraint(self): gt_constraint = models.CheckConstraint( - check=models.Q(pink__gt=2), name="constraint_pony_pink_gt_2" + condition=models.Q(pink__gt=2), name="constraint_pony_pink_gt_2" ) self.assertOptimizesTo( [ @@ -1329,7 +1329,7 @@ class OptimizerTests(SimpleTestCase): def test_create_model_add_constraint(self): gt_constraint = models.CheckConstraint( - check=models.Q(weight__gt=0), name="pony_weight_gt_0" + condition=models.Q(weight__gt=0), name="pony_weight_gt_0" ) self.assertOptimizesTo( [ @@ -1363,7 +1363,8 @@ class OptimizerTests(SimpleTestCase): options={ "constraints": [ models.CheckConstraint( - check=models.Q(weight__gt=0), name="pony_weight_gt_0" + condition=models.Q(weight__gt=0), + name="pony_weight_gt_0", ), models.UniqueConstraint( "weight", name="pony_weight_unique" diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py index 686eba4500..46dff01417 100644 --- a/tests/migrations/test_state.py +++ b/tests/migrations/test_state.py @@ -1887,7 +1887,9 @@ class ModelStateTests(SimpleTestCase): class Meta: constraints = [ - models.CheckConstraint(check=models.Q(size__gt=1), name="size_gt_1") + models.CheckConstraint( + condition=models.Q(size__gt=1), name="size_gt_1" + ) ] state = ModelState.from_model(ModelWithConstraints) diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py index ee0be3cbb3..b3de53efd7 100644 --- a/tests/postgres_tests/test_constraints.py +++ b/tests/postgres_tests/test_constraints.py @@ -59,7 +59,7 @@ class SchemaTests(PostgreSQLTestCase): constraint_name, self.get_constraints(RangesModel._meta.db_table) ) constraint = CheckConstraint( - check=Q(ints__contained_by=NumericRange(10, 30)), + condition=Q(ints__contained_by=NumericRange(10, 30)), name=constraint_name, ) with connection.schema_editor() as editor: @@ -71,7 +71,7 @@ class SchemaTests(PostgreSQLTestCase): def test_check_constraint_array_contains(self): constraint = CheckConstraint( - check=Q(field__contains=[1]), + condition=Q(field__contains=[1]), name="array_contains", ) msg = f"Constraint “{constraint.name}” is violated." @@ -81,7 +81,7 @@ class SchemaTests(PostgreSQLTestCase): def test_check_constraint_array_length(self): constraint = CheckConstraint( - check=Q(field__len=1), + condition=Q(field__len=1), name="array_length", ) msg = f"Constraint “{constraint.name}” is violated." @@ -95,7 +95,7 @@ class SchemaTests(PostgreSQLTestCase): constraint_name, self.get_constraints(RangesModel._meta.db_table) ) constraint = CheckConstraint( - check=Q(dates__contains=F("dates_inner")), + condition=Q(dates__contains=F("dates_inner")), name=constraint_name, ) with connection.schema_editor() as editor: @@ -119,7 +119,7 @@ class SchemaTests(PostgreSQLTestCase): constraint_name, self.get_constraints(RangesModel._meta.db_table) ) constraint = CheckConstraint( - check=Q(timestamps__contains=F("timestamps_inner")), + condition=Q(timestamps__contains=F("timestamps_inner")), name=constraint_name, ) with connection.schema_editor() as editor: @@ -139,7 +139,7 @@ class SchemaTests(PostgreSQLTestCase): def test_check_constraint_range_contains(self): constraint = CheckConstraint( - check=Q(ints__contains=(1, 5)), + condition=Q(ints__contains=(1, 5)), name="ints_contains", ) msg = f"Constraint “{constraint.name}” is violated." @@ -148,7 +148,7 @@ class SchemaTests(PostgreSQLTestCase): def test_check_constraint_range_lower_upper(self): constraint = CheckConstraint( - check=Q(ints__startswith__gte=0) & Q(ints__endswith__lte=99), + condition=Q(ints__startswith__gte=0) & Q(ints__endswith__lte=99), name="ints_range_lower_upper", ) msg = f"Constraint “{constraint.name}” is violated." @@ -160,12 +160,12 @@ class SchemaTests(PostgreSQLTestCase): def test_check_constraint_range_lower_with_nulls(self): constraint = CheckConstraint( - check=Q(ints__isnull=True) | Q(ints__startswith__gte=0), + condition=Q(ints__isnull=True) | Q(ints__startswith__gte=0), name="ints_optional_positive_range", ) constraint.validate(RangesModel, RangesModel()) constraint = CheckConstraint( - check=Q(ints__startswith__gte=0), + condition=Q(ints__startswith__gte=0), name="ints_positive_range", ) constraint.validate(RangesModel, RangesModel()) diff --git a/tests/postgres_tests/test_operations.py b/tests/postgres_tests/test_operations.py index bc2ae42096..5780348251 100644 --- a/tests/postgres_tests/test_operations.py +++ b/tests/postgres_tests/test_operations.py @@ -496,7 +496,7 @@ class AddConstraintNotValidTests(OperationTestBase): def test_add(self): table_name = f"{self.app_label}_pony" constraint_name = "pony_pink_gte_check" - constraint = CheckConstraint(check=Q(pink__gte=4), name=constraint_name) + constraint = CheckConstraint(condition=Q(pink__gte=4), name=constraint_name) operation = AddConstraintNotValid("Pony", constraint=constraint) project_state, new_state = self.make_test_state(self.app_label, operation) self.assertEqual( @@ -549,7 +549,7 @@ class ValidateConstraintTests(OperationTestBase): def test_validate(self): constraint_name = "pony_pink_gte_check" - constraint = CheckConstraint(check=Q(pink__gte=4), name=constraint_name) + constraint = CheckConstraint(condition=Q(pink__gte=4), name=constraint_name) operation = AddConstraintNotValid("Pony", constraint=constraint) project_state, new_state = self.make_test_state(self.app_label, operation) Pony = new_state.apps.get_model(self.app_label, "Pony") diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 52f5f289a1..b912d353eb 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -2791,7 +2791,7 @@ class SchemaTests(TransactionTestCase): self.isolated_local_models = [DurationModel] constraint_name = "duration_gte_5_minutes" constraint = CheckConstraint( - check=Q(duration__gt=datetime.timedelta(minutes=5)), + condition=Q(duration__gt=datetime.timedelta(minutes=5)), name=constraint_name, ) DurationModel._meta.constraints = [constraint] @@ -2821,7 +2821,7 @@ class SchemaTests(TransactionTestCase): self.isolated_local_models = [JSONConstraintModel] constraint_name = "check_only_stable_version" constraint = CheckConstraint( - check=Q(data__version="stable"), + condition=Q(data__version="stable"), name=constraint_name, ) JSONConstraintModel._meta.constraints = [constraint] @@ -2845,7 +2845,7 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) # Add the custom check constraint constraint = CheckConstraint( - check=Q(height__gte=0), name="author_height_gte_0_check" + condition=Q(height__gte=0), name="author_height_gte_0_check" ) custom_constraint_name = constraint.name Author._meta.constraints = [constraint] @@ -3256,7 +3256,9 @@ class SchemaTests(TransactionTestCase): "supports_column_check_constraints", "can_introspect_check_constraints" ) def test_composed_check_constraint_with_fk(self): - constraint = CheckConstraint(check=Q(author__gt=0), name="book_author_check") + constraint = CheckConstraint( + condition=Q(author__gt=0), name="book_author_check" + ) self._test_composed_constraint_with_fk(constraint) @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields") diff --git a/tests/validation/models.py b/tests/validation/models.py index 612a8dd63a..f6b1e0cd62 100644 --- a/tests/validation/models.py +++ b/tests/validation/models.py @@ -173,7 +173,7 @@ class Product(models.Model): } constraints = [ models.CheckConstraint( - check=models.Q(price__gt=models.F("discounted_price")), + condition=models.Q(price__gt=models.F("discounted_price")), name="price_gt_discounted_price_validation", ), ]