diff options
| author | Gagaro <gagaro42@gmail.com> | 2022-01-31 16:04:13 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-05-10 11:22:23 +0200 |
| commit | 667105877e6723c6985399803a364848891513cc (patch) | |
| tree | b6b3a9fe9f2c8767bc6f6a68f0580eef021b2b55 /docs | |
| parent | 441103a04d1d167dc870eaaf90e3fba974f67c93 (diff) | |
Fixed #30581 -- Added support for Meta.constraints validation.
Thanks Simon Charette, Keryn Knight, and Mariusz Felisiak for reviews.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/ref/checks.txt | 2 | ||||
| -rw-r--r-- | docs/ref/contrib/postgres/constraints.txt | 19 | ||||
| -rw-r--r-- | docs/ref/models/constraints.txt | 74 | ||||
| -rw-r--r-- | docs/ref/models/instances.txt | 59 | ||||
| -rw-r--r-- | docs/releases/4.1.txt | 13 |
5 files changed, 140 insertions, 27 deletions
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 3228d71f08..233d999fe7 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -395,6 +395,8 @@ Models * **models.W043**: ``<database>`` does not support indexes on expressions. * **models.W044**: ``<database>`` does not support unique constraints on expressions. +* **models.W045**: Check constraint ``<constraint>`` contains ``RawSQL()`` + expression and won't be validated during the model ``full_clean()``. Security -------- diff --git a/docs/ref/contrib/postgres/constraints.txt b/docs/ref/contrib/postgres/constraints.txt index 1c283c367e..26c6356693 100644 --- a/docs/ref/contrib/postgres/constraints.txt +++ b/docs/ref/contrib/postgres/constraints.txt @@ -12,7 +12,7 @@ PostgreSQL supports additional data integrity constraints available from the ``ExclusionConstraint`` ======================= -.. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=()) +.. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None) Creates an exclusion constraint in the database. Internally, PostgreSQL implements exclusion constraints using indexes. The default index type is @@ -27,6 +27,14 @@ PostgreSQL supports additional data integrity constraints available from the :exc:`~django.db.IntegrityError` is raised. Similarly, when update conflicts with an existing row. + Exclusion constraints are checked during the :ref:`model validation + <validating-objects>`. + + .. versionchanged:: 4.1 + + In older versions, exclusion constraints were not checked during model + validation. + ``name`` -------- @@ -165,6 +173,15 @@ creates an exclusion constraint on ``circle`` using ``circle_ops``. :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` in :attr:`~ExclusionConstraint.expressions`. +``violation_error_message`` +--------------------------- + +.. versionadded:: 4.1 + +The error message used when ``ValidationError`` is raised during +:ref:`model validation <validating-objects>`. Defaults to +:attr:`.BaseConstraint.violation_error_message`. + Examples -------- diff --git a/docs/ref/models/constraints.txt b/docs/ref/models/constraints.txt index 52bb8b48e0..9b1e110f85 100644 --- a/docs/ref/models/constraints.txt +++ b/docs/ref/models/constraints.txt @@ -31,24 +31,21 @@ option. .. admonition:: Validation of Constraints - In general constraints are **not** checked during ``full_clean()``, and do - not raise ``ValidationError``\s. Rather you'll get a database integrity - error on ``save()``. ``UniqueConstraint``\s without a - :attr:`~UniqueConstraint.condition` (i.e. non-partial unique constraints) - and :attr:`~UniqueConstraint.expressions` (i.e. non-functional unique - constraints) are different in this regard, in that they leverage the - existing ``validate_unique()`` logic, and thus enable two-stage validation. - In addition to ``IntegrityError`` on ``save()``, ``ValidationError`` is - also raised during model validation when the ``UniqueConstraint`` is - violated. + Constraints are checked during the :ref:`model validation + <validating-objects>`. + +.. versionchanged:: 4.1 + + In older versions, constraints were not checked during model validation. ``BaseConstraint`` ================== -.. class:: BaseConstraint(name) +.. class:: BaseConstraint(name, violation_error_message=None) Base class for all constraints. Subclasses must implement - ``constraint_sql()``, ``create_sql()``, and ``remove_sql()`` methods. + ``constraint_sql()``, ``create_sql()``, ``remove_sql()`` and + ``validate()`` methods. All constraints have the following parameters in common: @@ -60,10 +57,37 @@ All constraints have the following parameters in common: The name of the constraint. You must always specify a unique name for the constraint. +``violation_error_message`` +--------------------------- + +.. versionadded:: 4.1 + +.. attribute:: BaseConstraint.violation_error_message + +The error message used when ``ValidationError`` is raised during +:ref:`model validation <validating-objects>`. Defaults to +``"Constraint ā%(name)sā is violated."``. + +``validate()`` +-------------- + +.. versionadded:: 4.1 + +.. method:: BaseConstraint.validate(model, instance, exclude=None, using=DEFAULT_DB_ALIAS) + +Validates that the constraint, defined on ``model``, is respected on the +``instance``. This will do a query on the database to ensure that the +constraint is respected. If fields in the ``exclude`` list are needed to +validate the constraint, the constraint is ignored. + +Raise a ``ValidationError`` if the constraint is violated. + +This method must be implemented by a subclass. + ``CheckConstraint`` =================== -.. class:: CheckConstraint(*, check, name) +.. class:: CheckConstraint(*, check, name, violation_error_message=None) Creates a check constraint in the database. @@ -78,10 +102,14 @@ specifies the check you want the constraint to enforce. For example, ``CheckConstraint(check=Q(age__gte=18), name='age_gte_18')`` ensures the age field is never less than 18. +.. versionchanged:: 4.1 + + The ``violation_error_message`` argument was added. + ``UniqueConstraint`` ==================== -.. class:: UniqueConstraint(*expressions, fields=(), name=None, condition=None, deferrable=None, include=None, opclasses=()) +.. class:: UniqueConstraint(*expressions, fields=(), name=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None) Creates a unique constraint in the database. @@ -203,3 +231,21 @@ For example:: creates a unique index on ``username`` using ``varchar_pattern_ops``. ``opclasses`` are ignored for databases besides PostgreSQL. + +``violation_error_message`` +--------------------------- + +.. versionadded:: 4.1 + +.. attribute:: UniqueConstraint.violation_error_message + +The error message used when ``ValidationError`` is raised during +:ref:`model validation <validating-objects>`. Defaults to +:attr:`.BaseConstraint.violation_error_message`. + +This message is *not used* for :class:`UniqueConstraint`\s with +:attr:`~UniqueConstraint.fields` and without a +:attr:`~UniqueConstraint.condition`. Such :class:`~UniqueConstraint`\s show the +same message as constraints defined with +:attr:`.Field.unique` or in +:attr:`Meta.unique_together <django.db.models.Options.constraints>`. diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index e98377b8bf..b3323eaa0e 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -198,9 +198,10 @@ There are three steps involved in validating a model: 1. Validate the model fields - :meth:`Model.clean_fields()` 2. Validate the model as a whole - :meth:`Model.clean()` 3. Validate the field uniqueness - :meth:`Model.validate_unique()` +4. Validate the constraints - :meth:`Model.validate_constraints` -All three steps are performed when you call a model's -:meth:`~Model.full_clean()` method. +All four steps are performed when you call a model's :meth:`~Model.full_clean` +method. When you use a :class:`~django.forms.ModelForm`, the call to :meth:`~django.forms.Form.is_valid()` will perform these validation steps for @@ -210,12 +211,18 @@ need to call a model's :meth:`~Model.full_clean()` method if you plan to handle validation errors yourself, or if you have excluded fields from the :class:`~django.forms.ModelForm` that require validation. -.. method:: Model.full_clean(exclude=None, validate_unique=True) +.. versionchanged:: 4.1 -This method calls :meth:`Model.clean_fields()`, :meth:`Model.clean()`, and -:meth:`Model.validate_unique()` (if ``validate_unique`` is ``True``), in that -order and raises a :exc:`~django.core.exceptions.ValidationError` that has a -``message_dict`` attribute containing errors from all three stages. + In older versions, constraints were not checked during the model + validation. + +.. method:: Model.full_clean(exclude=None, validate_unique=True, validate_constraints=True) + +This method calls :meth:`Model.clean_fields()`, :meth:`Model.clean()`, +:meth:`Model.validate_unique()` (if ``validate_unique`` is ``True``), and +:meth:`Model.validate_constraints()` (if ``validate_constraints`` is ``True``) +in that order and raises a :exc:`~django.core.exceptions.ValidationError` that +has a ``message_dict`` attribute containing errors from all four stages. The optional ``exclude`` argument can be used to provide a list of field names that can be excluded from validation and cleaning. @@ -238,6 +245,10 @@ models. For example:: The first step ``full_clean()`` performs is to clean each individual field. +.. versionchanged:: 4.1 + + The ``validate_constraints`` argument was added. + .. method:: Model.clean_fields(exclude=None) This method will validate all fields on your model. The optional ``exclude`` @@ -306,7 +317,7 @@ pass a dictionary mapping field names to errors:: 'pub_date': ValidationError(_('Invalid date.'), code='invalid'), }) -Finally, ``full_clean()`` will check any unique constraints on your model. +Then, ``full_clean()`` will check unique constraints on your model. .. admonition:: How to raise field-specific validation errors if those fields don't appear in a ``ModelForm`` @@ -339,16 +350,40 @@ Finally, ``full_clean()`` will check any unique constraints on your model. .. method:: Model.validate_unique(exclude=None) -This method is similar to :meth:`~Model.clean_fields`, but validates all -uniqueness constraints on your model instead of individual field values. The -optional ``exclude`` argument allows you to provide a list of field names to -exclude from validation. It will raise a +This method is similar to :meth:`~Model.clean_fields`, but validates +uniqueness constraints defined via :attr:`.Field.unique`, +:attr:`.Field.unique_for_date`, :attr:`.Field.unique_for_month`, +:attr:`.Field.unique_for_year`, or :attr:`Meta.unique_together +<django.db.models.Options.unique_together>` on your model instead of individual +field values. The optional ``exclude`` argument allows you to provide a list of +field names to exclude from validation. It will raise a :exc:`~django.core.exceptions.ValidationError` if any fields fail validation. +:class:`~django.db.models.UniqueConstraint`\s defined in the +:attr:`Meta.constraints <django.db.models.Options.constraints>` are validated +by :meth:`Model.validate_constraints`. + Note that if you provide an ``exclude`` argument to ``validate_unique()``, any :attr:`~django.db.models.Options.unique_together` constraint involving one of the fields you provided will not be checked. +Finally, ``full_clean()`` will check any other constraints on your model. + +.. versionchanged:: 4.1 + + In older versions, :class:`~django.db.models.UniqueConstraint`\s were + validated by ``validate_unique()``. + +.. method:: Model.validate_constraints(exclude=None) + +.. versionadded:: 4.1 + +This method validates all constraints defined in +:attr:`Meta.constraints <django.db.models.Options.constraints>`. The +optional ``exclude`` argument allows you to provide a list of field names to +exclude from validation. It will raise a +:exc:`~django.core.exceptions.ValidationError` if any constraints fail +validation. Saving objects ============== diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 1bd3fe68b0..fa5990393b 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -65,6 +65,15 @@ advantage of developments in the ORM's asynchronous support as it evolves. See :ref:`async-queries` for details and limitations. +Validation of Constraints +------------------------- + +:class:`Check <django.db.models.CheckConstraint>`, +:class:`unique <django.db.models.UniqueConstraint>`, and :class:`exclusion +<django.contrib.postgres.constraints.ExclusionConstraint>` constraints defined +in the :attr:`Meta.constraints <django.db.models.Options.constraints>` option +are now checked during :ref:`model validation <validating-objects>`. + .. _csrf-cookie-masked-usage: ``CSRF_COOKIE_MASKED`` setting @@ -551,6 +560,10 @@ Miscellaneous * The undocumented ``django.contrib.auth.views.SuccessURLAllowedHostsMixin`` mixin is replaced by ``RedirectURLMixin``. +* :class:`~django.db.models.BaseConstraint` subclasses must implement + :meth:`~django.db.models.BaseConstraint.validate` method to allow those + constraints to be used for validation. + .. _deprecated-features-4.1: Features deprecated in 4.1 |
