mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
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.
This commit is contained in:
parent
f82c67aa21
commit
daf7d482db
@ -134,13 +134,30 @@ class BaseConstraint:
|
|||||||
|
|
||||||
|
|
||||||
class CheckConstraint(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__(
|
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 check is not None:
|
||||||
if not getattr(check, "conditional", False):
|
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(
|
raise TypeError(
|
||||||
"CheckConstraint.check must be a Q instance or boolean expression."
|
"CheckConstraint.condition must be a Q instance or boolean expression."
|
||||||
)
|
)
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name=name,
|
name=name,
|
||||||
@ -148,6 +165,24 @@ class CheckConstraint(BaseConstraint):
|
|||||||
violation_error_message=violation_error_message,
|
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):
|
def _check(self, model, connection):
|
||||||
errors = []
|
errors = []
|
||||||
if not (
|
if not (
|
||||||
@ -167,10 +202,10 @@ class CheckConstraint(BaseConstraint):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
references = set()
|
references = set()
|
||||||
check = self.check
|
condition = self.condition
|
||||||
if isinstance(check, Q):
|
if isinstance(condition, Q):
|
||||||
references.update(model._get_expr_references(check))
|
references.update(model._get_expr_references(condition))
|
||||||
if any(isinstance(expr, RawSQL) for expr in check.flatten()):
|
if any(isinstance(expr, RawSQL) for expr in condition.flatten()):
|
||||||
errors.append(
|
errors.append(
|
||||||
checks.Warning(
|
checks.Warning(
|
||||||
f"Check constraint {self.name!r} contains RawSQL() expression "
|
f"Check constraint {self.name!r} contains RawSQL() expression "
|
||||||
@ -185,7 +220,7 @@ class CheckConstraint(BaseConstraint):
|
|||||||
|
|
||||||
def _get_check_sql(self, model, schema_editor):
|
def _get_check_sql(self, model, schema_editor):
|
||||||
query = Query(model=model, alias_cols=False)
|
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)
|
compiler = query.get_compiler(connection=schema_editor.connection)
|
||||||
sql, params = where.as_sql(compiler, schema_editor.connection)
|
sql, params = where.as_sql(compiler, schema_editor.connection)
|
||||||
return sql % tuple(schema_editor.quote_value(p) for p in params)
|
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):
|
def validate(self, model, instance, exclude=None, using=DEFAULT_DB_ALIAS):
|
||||||
against = instance._get_field_value_map(meta=model._meta, exclude=exclude)
|
against = instance._get_field_value_map(meta=model._meta, exclude=exclude)
|
||||||
try:
|
try:
|
||||||
if not Q(self.check).check(against, using=using):
|
if not Q(self.condition).check(against, using=using):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
self.get_violation_error_message(), code=self.violation_error_code
|
self.get_violation_error_message(), code=self.violation_error_code
|
||||||
)
|
)
|
||||||
@ -212,9 +247,9 @@ class CheckConstraint(BaseConstraint):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: check=%s name=%s%s%s>" % (
|
return "<%s: condition=%s name=%s%s%s>" % (
|
||||||
self.__class__.__qualname__,
|
self.__class__.__qualname__,
|
||||||
self.check,
|
self.condition,
|
||||||
repr(self.name),
|
repr(self.name),
|
||||||
(
|
(
|
||||||
""
|
""
|
||||||
@ -233,7 +268,7 @@ class CheckConstraint(BaseConstraint):
|
|||||||
if isinstance(other, CheckConstraint):
|
if isinstance(other, CheckConstraint):
|
||||||
return (
|
return (
|
||||||
self.name == other.name
|
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_code == other.violation_error_code
|
||||||
and self.violation_error_message == other.violation_error_message
|
and self.violation_error_message == other.violation_error_message
|
||||||
)
|
)
|
||||||
@ -241,7 +276,7 @@ class CheckConstraint(BaseConstraint):
|
|||||||
|
|
||||||
def deconstruct(self):
|
def deconstruct(self):
|
||||||
path, args, kwargs = super().deconstruct()
|
path, args, kwargs = super().deconstruct()
|
||||||
kwargs["check"] = self.check
|
kwargs["condition"] = self.condition
|
||||||
return path, args, kwargs
|
return path, args, kwargs
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,6 +77,8 @@ details on these changes.
|
|||||||
* ``django.urls.register_converter()`` will no longer allow overriding existing
|
* ``django.urls.register_converter()`` will no longer allow overriding existing
|
||||||
converters.
|
converters.
|
||||||
|
|
||||||
|
* The ``check`` keyword argument of ``CheckConstraint`` will be removed.
|
||||||
|
|
||||||
.. _deprecation-removed-in-5.1:
|
.. _deprecation-removed-in-5.1:
|
||||||
|
|
||||||
5.1
|
5.1
|
||||||
|
@ -26,7 +26,7 @@ option.
|
|||||||
(including ``name``) each time. To work around name collisions, part of the
|
(including ``name``) each time. To work around name collisions, part of the
|
||||||
name may contain ``'%(app_label)s'`` and ``'%(class)s'``, which are
|
name may contain ``'%(app_label)s'`` and ``'%(class)s'``, which are
|
||||||
replaced, respectively, by the lowercased app label and class name of the
|
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')``.
|
name='%(app_label)s_%(class)s_is_adult')``.
|
||||||
|
|
||||||
.. admonition:: Validation of Constraints
|
.. admonition:: Validation of Constraints
|
||||||
@ -104,19 +104,19 @@ This method must be implemented by a subclass.
|
|||||||
``CheckConstraint``
|
``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.
|
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
|
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.
|
ensures the age field is never less than 18.
|
||||||
|
|
||||||
.. admonition:: Expression order
|
.. 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::
|
reasons. For example, use the following format if order matters::
|
||||||
|
|
||||||
CheckConstraint(
|
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",
|
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``
|
to behave the same as check constraints validation. For example, if ``age``
|
||||||
is a nullable field::
|
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``
|
``UniqueConstraint``
|
||||||
====================
|
====================
|
||||||
|
@ -467,7 +467,7 @@ not be looking at your Django code. For example::
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
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``
|
``verbose_name``
|
||||||
|
@ -388,7 +388,7 @@ Models
|
|||||||
``OneToOneField`` emulates the behavior of the SQL constraint ``ON DELETE
|
``OneToOneField`` emulates the behavior of the SQL constraint ``ON DELETE
|
||||||
RESTRICT``.
|
RESTRICT``.
|
||||||
|
|
||||||
* :attr:`.CheckConstraint.check` now supports boolean expressions.
|
* ``CheckConstraint.check`` now supports boolean expressions.
|
||||||
|
|
||||||
* The :meth:`.RelatedManager.add`, :meth:`~.RelatedManager.create`, and
|
* The :meth:`.RelatedManager.add`, :meth:`~.RelatedManager.create`, and
|
||||||
:meth:`~.RelatedManager.set` methods now accept callables as values in the
|
:meth:`~.RelatedManager.set` methods now accept callables as values in the
|
||||||
|
@ -422,6 +422,9 @@ Miscellaneous
|
|||||||
* Overriding existing converters with ``django.urls.register_converter()`` is
|
* Overriding existing converters with ``django.urls.register_converter()`` is
|
||||||
deprecated.
|
deprecated.
|
||||||
|
|
||||||
|
* The ``check`` keyword argument of ``CheckConstraint`` is deprecated in favor
|
||||||
|
of ``condition``.
|
||||||
|
|
||||||
Features removed in 5.1
|
Features removed in 5.1
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
|
@ -1443,7 +1443,7 @@ class ChangeListTests(TestCase):
|
|||||||
["field_3", "related_4_id"],
|
["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(
|
models.UniqueConstraint(
|
||||||
fields=["field_5"],
|
fields=["field_5"],
|
||||||
condition=models.Q(id__gt=10),
|
condition=models.Q(id__gt=10),
|
||||||
|
@ -287,8 +287,8 @@ class ConstraintNameTests(TestCase):
|
|||||||
class Model(models.Model):
|
class Model(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(check=models.Q(id__gt=0), name="foo"),
|
models.CheckConstraint(condition=models.Q(id__gt=0), name="foo"),
|
||||||
models.CheckConstraint(check=models.Q(id__lt=100), name="foo"),
|
models.CheckConstraint(condition=models.Q(id__lt=100), name="foo"),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -303,7 +303,7 @@ class ConstraintNameTests(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_collision_in_different_models(self):
|
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 Model1(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -328,7 +328,7 @@ class ConstraintNameTests(TestCase):
|
|||||||
class AbstractModel(models.Model):
|
class AbstractModel(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(check=models.Q(id__gt=0), name="foo")
|
models.CheckConstraint(condition=models.Q(id__gt=0), name="foo")
|
||||||
]
|
]
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ class ConstraintNameTests(TestCase):
|
|||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
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
|
abstract = True
|
||||||
@ -370,7 +370,7 @@ class ConstraintNameTests(TestCase):
|
|||||||
@modify_settings(INSTALLED_APPS={"append": "basic"})
|
@modify_settings(INSTALLED_APPS={"append": "basic"})
|
||||||
@isolate_apps("basic", "check_framework", kwarg_name="apps")
|
@isolate_apps("basic", "check_framework", kwarg_name="apps")
|
||||||
def test_collision_across_apps(self, 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 Model1(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -397,7 +397,7 @@ class ConstraintNameTests(TestCase):
|
|||||||
@isolate_apps("basic", "check_framework", kwarg_name="apps")
|
@isolate_apps("basic", "check_framework", kwarg_name="apps")
|
||||||
def test_no_collision_across_apps_interpolation(self, apps):
|
def test_no_collision_across_apps_interpolation(self, apps):
|
||||||
constraint = models.CheckConstraint(
|
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):
|
class Model1(models.Model):
|
||||||
|
@ -12,15 +12,15 @@ class Product(models.Model):
|
|||||||
}
|
}
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
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",
|
name="price_gt_discounted_price",
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(price__gt=0),
|
condition=models.Q(price__gt=0),
|
||||||
name="%(app_label)s_%(class)s_price_gt_0",
|
name="%(app_label)s_%(class)s_price_gt_0",
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(
|
condition=models.Q(
|
||||||
models.Q(unit__isnull=True) | models.Q(unit__in=["μg/mL", "ng/mL"])
|
models.Q(unit__isnull=True) | models.Q(unit__in=["μg/mL", "ng/mL"])
|
||||||
),
|
),
|
||||||
name="unicode_unit_list",
|
name="unicode_unit_list",
|
||||||
@ -113,7 +113,7 @@ class AbstractModel(models.Model):
|
|||||||
}
|
}
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(age__gte=18),
|
condition=models.Q(age__gte=18),
|
||||||
name="%(app_label)s_%(class)s_adult",
|
name="%(app_label)s_%(class)s_adult",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -123,104 +123,108 @@ class CheckConstraintTests(TestCase):
|
|||||||
check1 = models.Q(price__gt=models.F("discounted_price"))
|
check1 = models.Q(price__gt=models.F("discounted_price"))
|
||||||
check2 = models.Q(price__lt=models.F("discounted_price"))
|
check2 = models.Q(price__lt=models.F("discounted_price"))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
models.CheckConstraint(check=check1, name="price"),
|
models.CheckConstraint(condition=check1, name="price"),
|
||||||
models.CheckConstraint(check=check1, name="price"),
|
models.CheckConstraint(condition=check1, name="price"),
|
||||||
)
|
)
|
||||||
self.assertEqual(models.CheckConstraint(check=check1, name="price"), mock.ANY)
|
self.assertEqual(
|
||||||
self.assertNotEqual(
|
models.CheckConstraint(condition=check1, name="price"), mock.ANY
|
||||||
models.CheckConstraint(check=check1, name="price"),
|
|
||||||
models.CheckConstraint(check=check1, name="price2"),
|
|
||||||
)
|
)
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
models.CheckConstraint(check=check1, name="price"),
|
models.CheckConstraint(condition=check1, name="price"),
|
||||||
models.CheckConstraint(check=check2, name="price"),
|
models.CheckConstraint(condition=check1, name="price2"),
|
||||||
)
|
)
|
||||||
self.assertNotEqual(models.CheckConstraint(check=check1, name="price"), 1)
|
|
||||||
self.assertNotEqual(
|
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(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_message="custom error"
|
condition=check1, name="price", violation_error_message="custom error"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_message="custom error"
|
condition=check1, name="price", violation_error_message="custom error"
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_message="other custom error"
|
condition=check1,
|
||||||
|
name="price",
|
||||||
|
violation_error_message="other custom error",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_message="custom error"
|
condition=check1, name="price", violation_error_message="custom error"
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_message="custom error"
|
condition=check1, name="price", violation_error_message="custom error"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
models.CheckConstraint(check=check1, name="price"),
|
models.CheckConstraint(condition=check1, name="price"),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_code="custom_code"
|
condition=check1, name="price", violation_error_code="custom_code"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_code="custom_code"
|
condition=check1, name="price", violation_error_code="custom_code"
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=check1, name="price", violation_error_code="custom_code"
|
condition=check1, name="price", violation_error_code="custom_code"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
constraint = models.CheckConstraint(
|
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",
|
name="price_gt_discounted_price",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
repr(constraint),
|
repr(constraint),
|
||||||
"<CheckConstraint: check=(AND: ('price__gt', F(discounted_price))) "
|
"<CheckConstraint: condition=(AND: ('price__gt', F(discounted_price))) "
|
||||||
"name='price_gt_discounted_price'>",
|
"name='price_gt_discounted_price'>",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_repr_with_violation_error_message(self):
|
def test_repr_with_violation_error_message(self):
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.Q(price__lt=1),
|
condition=models.Q(price__lt=1),
|
||||||
name="price_lt_one",
|
name="price_lt_one",
|
||||||
violation_error_message="More than 1",
|
violation_error_message="More than 1",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
repr(constraint),
|
repr(constraint),
|
||||||
"<CheckConstraint: check=(AND: ('price__lt', 1)) name='price_lt_one' "
|
"<CheckConstraint: condition=(AND: ('price__lt', 1)) name='price_lt_one' "
|
||||||
"violation_error_message='More than 1'>",
|
"violation_error_message='More than 1'>",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_repr_with_violation_error_code(self):
|
def test_repr_with_violation_error_code(self):
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.Q(price__lt=1),
|
condition=models.Q(price__lt=1),
|
||||||
name="price_lt_one",
|
name="price_lt_one",
|
||||||
violation_error_code="more_than_one",
|
violation_error_code="more_than_one",
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
repr(constraint),
|
repr(constraint),
|
||||||
"<CheckConstraint: check=(AND: ('price__lt', 1)) name='price_lt_one' "
|
"<CheckConstraint: condition=(AND: ('price__lt', 1)) name='price_lt_one' "
|
||||||
"violation_error_code='more_than_one'>",
|
"violation_error_code='more_than_one'>",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_invalid_check_types(self):
|
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):
|
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):
|
def test_deconstruction(self):
|
||||||
check = models.Q(price__gt=models.F("discounted_price"))
|
check = models.Q(price__gt=models.F("discounted_price"))
|
||||||
name = "price_gt_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()
|
path, args, kwargs = constraint.deconstruct()
|
||||||
self.assertEqual(path, "django.db.models.CheckConstraint")
|
self.assertEqual(path, "django.db.models.CheckConstraint")
|
||||||
self.assertEqual(args, ())
|
self.assertEqual(args, ())
|
||||||
self.assertEqual(kwargs, {"check": check, "name": name})
|
self.assertEqual(kwargs, {"condition": check, "name": name})
|
||||||
|
|
||||||
@skipUnlessDBFeature("supports_table_check_constraints")
|
@skipUnlessDBFeature("supports_table_check_constraints")
|
||||||
def test_database_constraint(self):
|
def test_database_constraint(self):
|
||||||
@ -255,7 +259,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
|
|
||||||
def test_validate(self):
|
def test_validate(self):
|
||||||
check = models.Q(price__gt=models.F("discounted_price"))
|
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.
|
||||||
invalid_product = Product(price=10, discounted_price=42)
|
invalid_product = Product(price=10, discounted_price=42)
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
@ -276,7 +280,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
def test_validate_custom_error(self):
|
def test_validate_custom_error(self):
|
||||||
check = models.Q(price__gt=models.F("discounted_price"))
|
check = models.Q(price__gt=models.F("discounted_price"))
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=check,
|
condition=check,
|
||||||
name="price",
|
name="price",
|
||||||
violation_error_message="discount is fake",
|
violation_error_message="discount is fake",
|
||||||
violation_error_code="fake_discount",
|
violation_error_code="fake_discount",
|
||||||
@ -290,7 +294,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
|
|
||||||
def test_validate_boolean_expressions(self):
|
def test_validate_boolean_expressions(self):
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.expressions.ExpressionWrapper(
|
condition=models.expressions.ExpressionWrapper(
|
||||||
models.Q(price__gt=500) | models.Q(price__lt=500),
|
models.Q(price__gt=500) | models.Q(price__lt=500),
|
||||||
output_field=models.BooleanField(),
|
output_field=models.BooleanField(),
|
||||||
),
|
),
|
||||||
@ -304,7 +308,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
|
|
||||||
def test_validate_rawsql_expressions_noop(self):
|
def test_validate_rawsql_expressions_noop(self):
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.expressions.RawSQL(
|
condition=models.expressions.RawSQL(
|
||||||
"price < %s OR price > %s",
|
"price < %s OR price > %s",
|
||||||
(500, 500),
|
(500, 500),
|
||||||
output_field=models.BooleanField(),
|
output_field=models.BooleanField(),
|
||||||
@ -320,7 +324,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
def test_validate_nullable_field_with_none(self):
|
def test_validate_nullable_field_with_none(self):
|
||||||
# Nullable fields should be considered valid on None values.
|
# Nullable fields should be considered valid on None values.
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.Q(price__gte=0),
|
condition=models.Q(price__gte=0),
|
||||||
name="positive_price",
|
name="positive_price",
|
||||||
)
|
)
|
||||||
constraint.validate(Product, Product())
|
constraint.validate(Product, Product())
|
||||||
@ -328,7 +332,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
@skipIfDBFeature("supports_comparing_boolean_expr")
|
@skipIfDBFeature("supports_comparing_boolean_expr")
|
||||||
def test_validate_nullable_field_with_isnull(self):
|
def test_validate_nullable_field_with_isnull(self):
|
||||||
constraint = models.CheckConstraint(
|
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",
|
name="positive_price",
|
||||||
)
|
)
|
||||||
constraint.validate(Product, Product())
|
constraint.validate(Product, Product())
|
||||||
@ -336,11 +340,11 @@ class CheckConstraintTests(TestCase):
|
|||||||
@skipUnlessDBFeature("supports_json_field")
|
@skipUnlessDBFeature("supports_json_field")
|
||||||
def test_validate_nullable_jsonfield(self):
|
def test_validate_nullable_jsonfield(self):
|
||||||
is_null_constraint = models.CheckConstraint(
|
is_null_constraint = models.CheckConstraint(
|
||||||
check=models.Q(data__isnull=True),
|
condition=models.Q(data__isnull=True),
|
||||||
name="nullable_data",
|
name="nullable_data",
|
||||||
)
|
)
|
||||||
is_not_null_constraint = models.CheckConstraint(
|
is_not_null_constraint = models.CheckConstraint(
|
||||||
check=models.Q(data__isnull=False),
|
condition=models.Q(data__isnull=False),
|
||||||
name="nullable_data",
|
name="nullable_data",
|
||||||
)
|
)
|
||||||
is_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None))
|
is_null_constraint.validate(JSONFieldModel, JSONFieldModel(data=None))
|
||||||
@ -354,7 +358,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
|
|
||||||
def test_validate_pk_field(self):
|
def test_validate_pk_field(self):
|
||||||
constraint_with_pk = models.CheckConstraint(
|
constraint_with_pk = models.CheckConstraint(
|
||||||
check=~models.Q(pk=models.F("age")),
|
condition=~models.Q(pk=models.F("age")),
|
||||||
name="pk_not_age_check",
|
name="pk_not_age_check",
|
||||||
)
|
)
|
||||||
constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=2))
|
constraint_with_pk.validate(ChildModel, ChildModel(pk=1, age=2))
|
||||||
@ -369,7 +373,7 @@ class CheckConstraintTests(TestCase):
|
|||||||
def test_validate_jsonfield_exact(self):
|
def test_validate_jsonfield_exact(self):
|
||||||
data = {"release": "5.0.2", "version": "stable"}
|
data = {"release": "5.0.2", "version": "stable"}
|
||||||
json_exact_constraint = models.CheckConstraint(
|
json_exact_constraint = models.CheckConstraint(
|
||||||
check=models.Q(data__version="stable"),
|
condition=models.Q(data__version="stable"),
|
||||||
name="only_stable_version",
|
name="only_stable_version",
|
||||||
)
|
)
|
||||||
json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data))
|
json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data))
|
||||||
@ -379,6 +383,19 @@ class CheckConstraintTests(TestCase):
|
|||||||
with self.assertRaisesMessage(ValidationError, msg):
|
with self.assertRaisesMessage(ValidationError, msg):
|
||||||
json_exact_constraint.validate(JSONFieldModel, JSONFieldModel(data=data))
|
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):
|
class UniqueConstraintTests(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -270,7 +270,7 @@ class OperationTests(OperationTestCase):
|
|||||||
Neighborhood = self.current_state.apps.get_model("gis", "Neighborhood")
|
Neighborhood = self.current_state.apps.get_model("gis", "Neighborhood")
|
||||||
poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
|
poly = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
|
||||||
constraint = models.CheckConstraint(
|
constraint = models.CheckConstraint(
|
||||||
check=models.Q(geom=poly),
|
condition=models.Q(geom=poly),
|
||||||
name="geom_within_constraint",
|
name="geom_within_constraint",
|
||||||
)
|
)
|
||||||
Neighborhood._meta.constraints = [constraint]
|
Neighborhood._meta.constraints = [constraint]
|
||||||
|
@ -84,7 +84,7 @@ class CheckConstraintModel(models.Model):
|
|||||||
}
|
}
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
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)
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1855,7 +1855,9 @@ class ConstraintsTests(TestCase):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
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)
|
errors = Model.check(databases=self.databases)
|
||||||
@ -1880,7 +1882,9 @@ class ConstraintsTests(TestCase):
|
|||||||
class Meta:
|
class Meta:
|
||||||
required_db_features = {"supports_table_check_constraints"}
|
required_db_features = {"supports_table_check_constraints"}
|
||||||
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), [])
|
self.assertEqual(Model.check(databases=self.databases), [])
|
||||||
@ -1892,7 +1896,7 @@ class ConstraintsTests(TestCase):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name",
|
name="name",
|
||||||
check=models.Q(missing_field=2),
|
condition=models.Q(missing_field=2),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1919,7 +1923,7 @@ class ConstraintsTests(TestCase):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(name="name", check=models.Q(parents=3)),
|
models.CheckConstraint(name="name", condition=models.Q(parents=3)),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1942,7 +1946,7 @@ class ConstraintsTests(TestCase):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name",
|
name="name",
|
||||||
check=models.Q(model__isnull=True),
|
condition=models.Q(model__isnull=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1964,7 +1968,7 @@ class ConstraintsTests(TestCase):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(name="name", check=models.Q(m2m=2)),
|
models.CheckConstraint(name="name", condition=models.Q(m2m=2)),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1992,7 +1996,7 @@ class ConstraintsTests(TestCase):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name",
|
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 = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name",
|
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:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(name="name", check=models.Q(field1=1)),
|
models.CheckConstraint(name="name", condition=models.Q(field1=1)),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -2053,20 +2057,21 @@ class ConstraintsTests(TestCase):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name1",
|
name="name1",
|
||||||
check=models.Q(
|
condition=models.Q(
|
||||||
field1__lt=models.F("parent__field1")
|
field1__lt=models.F("parent__field1")
|
||||||
+ models.F("parent__field2")
|
+ models.F("parent__field2")
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name2", check=models.Q(name=Lower("parent__name"))
|
name="name2", condition=models.Q(name=Lower("parent__name"))
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name3", check=models.Q(parent__field3=models.F("field1"))
|
name="name3",
|
||||||
|
condition=models.Q(parent__field3=models.F("field1")),
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name4",
|
name="name4",
|
||||||
check=models.Q(name=Lower("previous__name")),
|
condition=models.Q(name=Lower("previous__name")),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2100,7 +2105,7 @@ class ConstraintsTests(TestCase):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
name="name",
|
name="name",
|
||||||
check=models.Q(
|
condition=models.Q(
|
||||||
(
|
(
|
||||||
models.Q(name="test")
|
models.Q(name="test")
|
||||||
& models.Q(field1__lt=models.F("parent__field1"))
|
& models.Q(field1__lt=models.F("parent__field1"))
|
||||||
@ -2136,16 +2141,18 @@ class ConstraintsTests(TestCase):
|
|||||||
class Meta:
|
class Meta:
|
||||||
required_db_features = {"supports_table_check_constraints"}
|
required_db_features = {"supports_table_check_constraints"}
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(check=models.Q(id__gt=0), name="q_check"),
|
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.ExpressionWrapper(
|
condition=models.Q(id__gt=0), name="q_check"
|
||||||
|
),
|
||||||
|
models.CheckConstraint(
|
||||||
|
condition=models.ExpressionWrapper(
|
||||||
models.Q(price__gt=20),
|
models.Q(price__gt=20),
|
||||||
output_field=models.BooleanField(),
|
output_field=models.BooleanField(),
|
||||||
),
|
),
|
||||||
name="expression_wrapper_check",
|
name="expression_wrapper_check",
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.expressions.RawSQL(
|
condition=models.expressions.RawSQL(
|
||||||
"id = 0",
|
"id = 0",
|
||||||
params=(),
|
params=(),
|
||||||
output_field=models.BooleanField(),
|
output_field=models.BooleanField(),
|
||||||
@ -2153,7 +2160,7 @@ class ConstraintsTests(TestCase):
|
|||||||
name="raw_sql_check",
|
name="raw_sql_check",
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(
|
condition=models.Q(
|
||||||
models.ExpressionWrapper(
|
models.ExpressionWrapper(
|
||||||
models.Q(
|
models.Q(
|
||||||
models.expressions.RawSQL(
|
models.expressions.RawSQL(
|
||||||
|
@ -287,7 +287,7 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
{
|
{
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
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": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
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])
|
changes = self.get_changes([], [author])
|
||||||
constraint = models.CheckConstraint(
|
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?
|
# Right number of migrations?
|
||||||
self.assertEqual(len(changes["otherapp"]), 1)
|
self.assertEqual(len(changes["otherapp"]), 1)
|
||||||
@ -2789,7 +2790,7 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
self.assertNumberMigrations(changes, "testapp", 1)
|
self.assertNumberMigrations(changes, "testapp", 1)
|
||||||
self.assertOperationTypes(changes, "testapp", 0, ["AddConstraint"])
|
self.assertOperationTypes(changes, "testapp", 0, ["AddConstraint"])
|
||||||
added_constraint = models.CheckConstraint(
|
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(
|
self.assertOperationAttributes(
|
||||||
changes, "testapp", 0, 0, model_name="author", constraint=added_constraint
|
changes, "testapp", 0, 0, model_name="author", constraint=added_constraint
|
||||||
@ -2838,7 +2839,7 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
{
|
{
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(type__in=book_types.keys()),
|
condition=models.Q(type__in=book_types.keys()),
|
||||||
name="book_type_check",
|
name="book_type_check",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -2854,7 +2855,7 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
{
|
{
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(("type__in", tuple(book_types))),
|
condition=models.Q(("type__in", tuple(book_types))),
|
||||||
name="book_type_check",
|
name="book_type_check",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -4168,7 +4169,7 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
"order_with_respect_to": "book",
|
"order_with_respect_to": "book",
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
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",
|
"order_with_respect_to": "book",
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
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": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(_order__gt=1),
|
condition=models.Q(_order__gt=1),
|
||||||
name="book_order_gt_1",
|
name="book_order_gt_1",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -441,7 +441,7 @@ class OperationTests(OperationTestBase):
|
|||||||
def test_create_model_with_constraint(self):
|
def test_create_model_with_constraint(self):
|
||||||
where = models.Q(pink__gt=2)
|
where = models.Q(pink__gt=2)
|
||||||
check_constraint = models.CheckConstraint(
|
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(
|
operation = migrations.CreateModel(
|
||||||
"Pony",
|
"Pony",
|
||||||
@ -484,13 +484,13 @@ class OperationTests(OperationTestBase):
|
|||||||
def test_create_model_with_boolean_expression_in_check_constraint(self):
|
def test_create_model_with_boolean_expression_in_check_constraint(self):
|
||||||
app_label = "test_crmobechc"
|
app_label = "test_crmobechc"
|
||||||
rawsql_constraint = models.CheckConstraint(
|
rawsql_constraint = models.CheckConstraint(
|
||||||
check=models.expressions.RawSQL(
|
condition=models.expressions.RawSQL(
|
||||||
"price < %s", (1000,), output_field=models.BooleanField()
|
"price < %s", (1000,), output_field=models.BooleanField()
|
||||||
),
|
),
|
||||||
name=f"{app_label}_price_lt_1000_raw",
|
name=f"{app_label}_price_lt_1000_raw",
|
||||||
)
|
)
|
||||||
wrapper_constraint = models.CheckConstraint(
|
wrapper_constraint = models.CheckConstraint(
|
||||||
check=models.expressions.ExpressionWrapper(
|
condition=models.expressions.ExpressionWrapper(
|
||||||
models.Q(price__gt=500) | models.Q(price__lt=500),
|
models.Q(price__gt=500) | models.Q(price__lt=500),
|
||||||
output_field=models.BooleanField(),
|
output_field=models.BooleanField(),
|
||||||
),
|
),
|
||||||
@ -3858,7 +3858,7 @@ class OperationTests(OperationTestBase):
|
|||||||
project_state = self.set_up_test_model("test_addconstraint")
|
project_state = self.set_up_test_model("test_addconstraint")
|
||||||
gt_check = models.Q(pink__gt=2)
|
gt_check = models.Q(pink__gt=2)
|
||||||
gt_constraint = models.CheckConstraint(
|
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)
|
gt_operation = migrations.AddConstraint("Pony", gt_constraint)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -3901,7 +3901,7 @@ class OperationTests(OperationTestBase):
|
|||||||
# Add another one.
|
# Add another one.
|
||||||
lt_check = models.Q(pink__lt=100)
|
lt_check = models.Q(pink__lt=100)
|
||||||
lt_constraint = models.CheckConstraint(
|
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 = migrations.AddConstraint("Pony", lt_constraint)
|
||||||
lt_operation.state_forwards("test_addconstraint", new_state)
|
lt_operation.state_forwards("test_addconstraint", new_state)
|
||||||
@ -3981,8 +3981,8 @@ class OperationTests(OperationTestBase):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
for check, valid, invalid in checks:
|
for check, valid, invalid in checks:
|
||||||
with self.subTest(check=check, valid=valid, invalid=invalid):
|
with self.subTest(condition=check, valid=valid, invalid=invalid):
|
||||||
constraint = models.CheckConstraint(check=check, name="constraint")
|
constraint = models.CheckConstraint(condition=check, name="constraint")
|
||||||
operation = migrations.AddConstraint("Author", constraint)
|
operation = migrations.AddConstraint("Author", constraint)
|
||||||
to_state = from_state.clone()
|
to_state = from_state.clone()
|
||||||
operation.state_forwards(app_label, to_state)
|
operation.state_forwards(app_label, to_state)
|
||||||
@ -4006,7 +4006,7 @@ class OperationTests(OperationTestBase):
|
|||||||
constraint_name = "add_constraint_or"
|
constraint_name = "add_constraint_or"
|
||||||
from_state = self.set_up_test_model(app_label)
|
from_state = self.set_up_test_model(app_label)
|
||||||
check = models.Q(pink__gt=2, weight__gt=2) | models.Q(weight__lt=0)
|
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)
|
operation = migrations.AddConstraint("Pony", constraint)
|
||||||
to_state = from_state.clone()
|
to_state = from_state.clone()
|
||||||
operation.state_forwards(app_label, to_state)
|
operation.state_forwards(app_label, to_state)
|
||||||
@ -4040,7 +4040,7 @@ class OperationTests(OperationTestBase):
|
|||||||
]
|
]
|
||||||
from_state = self.apply_operations(app_label, ProjectState(), operations)
|
from_state = self.apply_operations(app_label, ProjectState(), operations)
|
||||||
constraint = models.CheckConstraint(
|
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",
|
name="test_addconstraint_combinable_sum_100",
|
||||||
)
|
)
|
||||||
operation = migrations.AddConstraint("Book", constraint)
|
operation = migrations.AddConstraint("Book", constraint)
|
||||||
@ -4058,11 +4058,11 @@ class OperationTests(OperationTestBase):
|
|||||||
"test_removeconstraint",
|
"test_removeconstraint",
|
||||||
constraints=[
|
constraints=[
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(pink__gt=2),
|
condition=models.Q(pink__gt=2),
|
||||||
name="test_remove_constraint_pony_pink_gt_2",
|
name="test_remove_constraint_pony_pink_gt_2",
|
||||||
),
|
),
|
||||||
models.CheckConstraint(
|
models.CheckConstraint(
|
||||||
check=models.Q(pink__lt=100),
|
condition=models.Q(pink__lt=100),
|
||||||
name="test_remove_constraint_pony_pink_lt_100",
|
name="test_remove_constraint_pony_pink_lt_100",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1208,7 +1208,7 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
|
|
||||||
def test_add_remove_constraint(self):
|
def test_add_remove_constraint(self):
|
||||||
gt_constraint = models.CheckConstraint(
|
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(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
@ -1329,7 +1329,7 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
|
|
||||||
def test_create_model_add_constraint(self):
|
def test_create_model_add_constraint(self):
|
||||||
gt_constraint = models.CheckConstraint(
|
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(
|
self.assertOptimizesTo(
|
||||||
[
|
[
|
||||||
@ -1363,7 +1363,8 @@ class OptimizerTests(SimpleTestCase):
|
|||||||
options={
|
options={
|
||||||
"constraints": [
|
"constraints": [
|
||||||
models.CheckConstraint(
|
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(
|
models.UniqueConstraint(
|
||||||
"weight", name="pony_weight_unique"
|
"weight", name="pony_weight_unique"
|
||||||
|
@ -1887,7 +1887,9 @@ class ModelStateTests(SimpleTestCase):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
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)
|
state = ModelState.from_model(ModelWithConstraints)
|
||||||
|
@ -59,7 +59,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
||||||
)
|
)
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(ints__contained_by=NumericRange(10, 30)),
|
condition=Q(ints__contained_by=NumericRange(10, 30)),
|
||||||
name=constraint_name,
|
name=constraint_name,
|
||||||
)
|
)
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
@ -71,7 +71,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_check_constraint_array_contains(self):
|
def test_check_constraint_array_contains(self):
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(field__contains=[1]),
|
condition=Q(field__contains=[1]),
|
||||||
name="array_contains",
|
name="array_contains",
|
||||||
)
|
)
|
||||||
msg = f"Constraint “{constraint.name}” is violated."
|
msg = f"Constraint “{constraint.name}” is violated."
|
||||||
@ -81,7 +81,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_check_constraint_array_length(self):
|
def test_check_constraint_array_length(self):
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(field__len=1),
|
condition=Q(field__len=1),
|
||||||
name="array_length",
|
name="array_length",
|
||||||
)
|
)
|
||||||
msg = f"Constraint “{constraint.name}” is violated."
|
msg = f"Constraint “{constraint.name}” is violated."
|
||||||
@ -95,7 +95,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
||||||
)
|
)
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(dates__contains=F("dates_inner")),
|
condition=Q(dates__contains=F("dates_inner")),
|
||||||
name=constraint_name,
|
name=constraint_name,
|
||||||
)
|
)
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
@ -119,7 +119,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
constraint_name, self.get_constraints(RangesModel._meta.db_table)
|
||||||
)
|
)
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(timestamps__contains=F("timestamps_inner")),
|
condition=Q(timestamps__contains=F("timestamps_inner")),
|
||||||
name=constraint_name,
|
name=constraint_name,
|
||||||
)
|
)
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
@ -139,7 +139,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_check_constraint_range_contains(self):
|
def test_check_constraint_range_contains(self):
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(ints__contains=(1, 5)),
|
condition=Q(ints__contains=(1, 5)),
|
||||||
name="ints_contains",
|
name="ints_contains",
|
||||||
)
|
)
|
||||||
msg = f"Constraint “{constraint.name}” is violated."
|
msg = f"Constraint “{constraint.name}” is violated."
|
||||||
@ -148,7 +148,7 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_check_constraint_range_lower_upper(self):
|
def test_check_constraint_range_lower_upper(self):
|
||||||
constraint = CheckConstraint(
|
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",
|
name="ints_range_lower_upper",
|
||||||
)
|
)
|
||||||
msg = f"Constraint “{constraint.name}” is violated."
|
msg = f"Constraint “{constraint.name}” is violated."
|
||||||
@ -160,12 +160,12 @@ class SchemaTests(PostgreSQLTestCase):
|
|||||||
|
|
||||||
def test_check_constraint_range_lower_with_nulls(self):
|
def test_check_constraint_range_lower_with_nulls(self):
|
||||||
constraint = CheckConstraint(
|
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",
|
name="ints_optional_positive_range",
|
||||||
)
|
)
|
||||||
constraint.validate(RangesModel, RangesModel())
|
constraint.validate(RangesModel, RangesModel())
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(ints__startswith__gte=0),
|
condition=Q(ints__startswith__gte=0),
|
||||||
name="ints_positive_range",
|
name="ints_positive_range",
|
||||||
)
|
)
|
||||||
constraint.validate(RangesModel, RangesModel())
|
constraint.validate(RangesModel, RangesModel())
|
||||||
|
@ -496,7 +496,7 @@ class AddConstraintNotValidTests(OperationTestBase):
|
|||||||
def test_add(self):
|
def test_add(self):
|
||||||
table_name = f"{self.app_label}_pony"
|
table_name = f"{self.app_label}_pony"
|
||||||
constraint_name = "pony_pink_gte_check"
|
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)
|
operation = AddConstraintNotValid("Pony", constraint=constraint)
|
||||||
project_state, new_state = self.make_test_state(self.app_label, operation)
|
project_state, new_state = self.make_test_state(self.app_label, operation)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -549,7 +549,7 @@ class ValidateConstraintTests(OperationTestBase):
|
|||||||
|
|
||||||
def test_validate(self):
|
def test_validate(self):
|
||||||
constraint_name = "pony_pink_gte_check"
|
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)
|
operation = AddConstraintNotValid("Pony", constraint=constraint)
|
||||||
project_state, new_state = self.make_test_state(self.app_label, operation)
|
project_state, new_state = self.make_test_state(self.app_label, operation)
|
||||||
Pony = new_state.apps.get_model(self.app_label, "Pony")
|
Pony = new_state.apps.get_model(self.app_label, "Pony")
|
||||||
|
@ -2791,7 +2791,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
self.isolated_local_models = [DurationModel]
|
self.isolated_local_models = [DurationModel]
|
||||||
constraint_name = "duration_gte_5_minutes"
|
constraint_name = "duration_gte_5_minutes"
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(duration__gt=datetime.timedelta(minutes=5)),
|
condition=Q(duration__gt=datetime.timedelta(minutes=5)),
|
||||||
name=constraint_name,
|
name=constraint_name,
|
||||||
)
|
)
|
||||||
DurationModel._meta.constraints = [constraint]
|
DurationModel._meta.constraints = [constraint]
|
||||||
@ -2821,7 +2821,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
self.isolated_local_models = [JSONConstraintModel]
|
self.isolated_local_models = [JSONConstraintModel]
|
||||||
constraint_name = "check_only_stable_version"
|
constraint_name = "check_only_stable_version"
|
||||||
constraint = CheckConstraint(
|
constraint = CheckConstraint(
|
||||||
check=Q(data__version="stable"),
|
condition=Q(data__version="stable"),
|
||||||
name=constraint_name,
|
name=constraint_name,
|
||||||
)
|
)
|
||||||
JSONConstraintModel._meta.constraints = [constraint]
|
JSONConstraintModel._meta.constraints = [constraint]
|
||||||
@ -2845,7 +2845,7 @@ class SchemaTests(TransactionTestCase):
|
|||||||
editor.create_model(Author)
|
editor.create_model(Author)
|
||||||
# Add the custom check constraint
|
# Add the custom check constraint
|
||||||
constraint = CheckConstraint(
|
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
|
custom_constraint_name = constraint.name
|
||||||
Author._meta.constraints = [constraint]
|
Author._meta.constraints = [constraint]
|
||||||
@ -3256,7 +3256,9 @@ class SchemaTests(TransactionTestCase):
|
|||||||
"supports_column_check_constraints", "can_introspect_check_constraints"
|
"supports_column_check_constraints", "can_introspect_check_constraints"
|
||||||
)
|
)
|
||||||
def test_composed_check_constraint_with_fk(self):
|
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)
|
self._test_composed_constraint_with_fk(constraint)
|
||||||
|
|
||||||
@skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
|
@skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
|
||||||
|
@ -173,7 +173,7 @@ class Product(models.Model):
|
|||||||
}
|
}
|
||||||
constraints = [
|
constraints = [
|
||||||
models.CheckConstraint(
|
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",
|
name="price_gt_discounted_price_validation",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user