mirror of
				https://github.com/django/django.git
				synced 2025-10-25 06:36:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ========================================
 | |
| PostgreSQL specific database constraints
 | |
| ========================================
 | |
| 
 | |
| .. module:: django.contrib.postgres.constraints
 | |
|    :synopsis: PostgreSQL specific database constraint
 | |
| 
 | |
| PostgreSQL supports additional data integrity constraints available from the
 | |
| ``django.contrib.postgres.constraints`` module. They are added in the model
 | |
| :attr:`Meta.constraints <django.db.models.Options.constraints>` option.
 | |
| 
 | |
| ``ExclusionConstraint``
 | |
| =======================
 | |
| 
 | |
| .. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, violation_error_code=None, violation_error_message=None)
 | |
| 
 | |
|     Creates an exclusion constraint in the database. Internally, PostgreSQL
 | |
|     implements exclusion constraints using indexes. The default index type is
 | |
|     `GiST <https://www.postgresql.org/docs/current/gist.html>`_. To use them,
 | |
|     you need to activate the `btree_gist extension
 | |
|     <https://www.postgresql.org/docs/current/btree-gist.html>`_ on PostgreSQL.
 | |
|     You can install it using the
 | |
|     :class:`~django.contrib.postgres.operations.BtreeGistExtension` migration
 | |
|     operation.
 | |
| 
 | |
|     If you attempt to insert a new row that conflicts with an existing row, an
 | |
|     :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>`.
 | |
| 
 | |
| ``name``
 | |
| --------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.name
 | |
| 
 | |
| See :attr:`.BaseConstraint.name`.
 | |
| 
 | |
| ``expressions``
 | |
| ---------------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.expressions
 | |
| 
 | |
| An iterable of 2-tuples. The first element is an expression or string. The
 | |
| second element is an SQL operator represented as a string. To avoid typos, you
 | |
| may use :class:`~django.contrib.postgres.fields.RangeOperators` which maps the
 | |
| operators with strings. For example::
 | |
| 
 | |
|     expressions = [
 | |
|         ("timespan", RangeOperators.ADJACENT_TO),
 | |
|         (F("room"), RangeOperators.EQUAL),
 | |
|     ]
 | |
| 
 | |
| .. admonition:: Restrictions on operators.
 | |
| 
 | |
|     Only commutative operators can be used in exclusion constraints.
 | |
| 
 | |
| The :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` expression can
 | |
| be used to specify a custom `operator class`_ for the constraint expressions.
 | |
| For example::
 | |
| 
 | |
|     expressions = [
 | |
|         (OpClass("circle", name="circle_ops"), RangeOperators.OVERLAPS),
 | |
|     ]
 | |
| 
 | |
| creates an exclusion constraint on ``circle`` using ``circle_ops``.
 | |
| 
 | |
| .. _operator class: https://www.postgresql.org/docs/current/indexes-opclass.html
 | |
| 
 | |
| ``index_type``
 | |
| --------------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.index_type
 | |
| 
 | |
| The index type of the constraint. Accepted values are ``GIST`` or ``SPGIST``.
 | |
| Matching is case insensitive. If not provided, the default index type is
 | |
| ``GIST``.
 | |
| 
 | |
| ``condition``
 | |
| -------------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.condition
 | |
| 
 | |
| A :class:`~django.db.models.Q` object that specifies the condition to restrict
 | |
| a constraint to a subset of rows. For example,
 | |
| ``condition=Q(cancelled=False)``.
 | |
| 
 | |
| These conditions have the same database restrictions as
 | |
| :attr:`django.db.models.Index.condition`.
 | |
| 
 | |
| ``deferrable``
 | |
| --------------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.deferrable
 | |
| 
 | |
| Set this parameter to create a deferrable exclusion constraint. Accepted values
 | |
| are ``Deferrable.DEFERRED`` or ``Deferrable.IMMEDIATE``. For example::
 | |
| 
 | |
|     from django.contrib.postgres.constraints import ExclusionConstraint
 | |
|     from django.contrib.postgres.fields import RangeOperators
 | |
|     from django.db.models import Deferrable
 | |
| 
 | |
| 
 | |
|     ExclusionConstraint(
 | |
|         name="exclude_overlapping_deferred",
 | |
|         expressions=[
 | |
|             ("timespan", RangeOperators.OVERLAPS),
 | |
|         ],
 | |
|         deferrable=Deferrable.DEFERRED,
 | |
|     )
 | |
| 
 | |
| By default constraints are not deferred. A deferred constraint will not be
 | |
| enforced until the end of the transaction. An immediate constraint will be
 | |
| enforced immediately after every command.
 | |
| 
 | |
| .. warning::
 | |
| 
 | |
|     Deferred exclusion constraints may lead to a `performance penalty
 | |
|     <https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4>`_.
 | |
| 
 | |
| ``include``
 | |
| -----------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.include
 | |
| 
 | |
| A list or tuple of the names of the fields to be included in the covering
 | |
| exclusion constraint as non-key columns. This allows index-only scans to be
 | |
| used for queries that select only included fields
 | |
| (:attr:`~ExclusionConstraint.include`) and filter only by indexed fields
 | |
| (:attr:`~ExclusionConstraint.expressions`).
 | |
| 
 | |
| ``include`` is supported for GiST indexes. PostgreSQL 14+ also supports
 | |
| ``include`` for SP-GiST indexes.
 | |
| 
 | |
| ``violation_error_code``
 | |
| ------------------------
 | |
| 
 | |
| .. attribute:: ExclusionConstraint.violation_error_code
 | |
| 
 | |
| The error code used when ``ValidationError`` is raised during
 | |
| :ref:`model validation <validating-objects>`. Defaults to ``None``.
 | |
| 
 | |
| ``violation_error_message``
 | |
| ---------------------------
 | |
| 
 | |
| The error message used when ``ValidationError`` is raised during
 | |
| :ref:`model validation <validating-objects>`. Defaults to
 | |
| :attr:`.BaseConstraint.violation_error_message`.
 | |
| 
 | |
| Examples
 | |
| --------
 | |
| 
 | |
| The following example restricts overlapping reservations in the same room, not
 | |
| taking canceled reservations into account::
 | |
| 
 | |
|     from django.contrib.postgres.constraints import ExclusionConstraint
 | |
|     from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
 | |
|     from django.db import models
 | |
|     from django.db.models import Q
 | |
| 
 | |
| 
 | |
|     class Room(models.Model):
 | |
|         number = models.IntegerField()
 | |
| 
 | |
| 
 | |
|     class Reservation(models.Model):
 | |
|         room = models.ForeignKey("Room", on_delete=models.CASCADE)
 | |
|         timespan = DateTimeRangeField()
 | |
|         cancelled = models.BooleanField(default=False)
 | |
| 
 | |
|         class Meta:
 | |
|             constraints = [
 | |
|                 ExclusionConstraint(
 | |
|                     name="exclude_overlapping_reservations",
 | |
|                     expressions=[
 | |
|                         ("timespan", RangeOperators.OVERLAPS),
 | |
|                         ("room", RangeOperators.EQUAL),
 | |
|                     ],
 | |
|                     condition=Q(cancelled=False),
 | |
|                 ),
 | |
|             ]
 | |
| 
 | |
| In case your model defines a range using two fields, instead of the native
 | |
| PostgreSQL range types, you should write an expression that uses the equivalent
 | |
| function (e.g. ``TsTzRange()``), and use the delimiters for the field. Most
 | |
| often, the delimiters will be ``'[)'``, meaning that the lower bound is
 | |
| inclusive and the upper bound is exclusive. You may use the
 | |
| :class:`~django.contrib.postgres.fields.RangeBoundary` that provides an
 | |
| expression mapping for the `range boundaries <https://www.postgresql.org/docs/
 | |
| current/rangetypes.html#RANGETYPES-INCLUSIVITY>`_. For example::
 | |
| 
 | |
|     from django.contrib.postgres.constraints import ExclusionConstraint
 | |
|     from django.contrib.postgres.fields import (
 | |
|         DateTimeRangeField,
 | |
|         RangeBoundary,
 | |
|         RangeOperators,
 | |
|     )
 | |
|     from django.db import models
 | |
|     from django.db.models import Func, Q
 | |
| 
 | |
| 
 | |
|     class TsTzRange(Func):
 | |
|         function = "TSTZRANGE"
 | |
|         output_field = DateTimeRangeField()
 | |
| 
 | |
| 
 | |
|     class Reservation(models.Model):
 | |
|         room = models.ForeignKey("Room", on_delete=models.CASCADE)
 | |
|         start = models.DateTimeField()
 | |
|         end = models.DateTimeField()
 | |
|         cancelled = models.BooleanField(default=False)
 | |
| 
 | |
|         class Meta:
 | |
|             constraints = [
 | |
|                 ExclusionConstraint(
 | |
|                     name="exclude_overlapping_reservations",
 | |
|                     expressions=[
 | |
|                         (
 | |
|                             TsTzRange("start", "end", RangeBoundary()),
 | |
|                             RangeOperators.OVERLAPS,
 | |
|                         ),
 | |
|                         ("room", RangeOperators.EQUAL),
 | |
|                     ],
 | |
|                     condition=Q(cancelled=False),
 | |
|                 ),
 | |
|             ]
 |