2
0

constraints.txt 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. ========================================
  2. PostgreSQL specific database constraints
  3. ========================================
  4. .. module:: django.contrib.postgres.constraints
  5. :synopsis: PostgreSQL specific database constraint
  6. PostgreSQL supports additional data integrity constraints available from the
  7. ``django.contrib.postgres.constraints`` module. They are added in the model
  8. :attr:`Meta.constraints <django.db.models.Options.constraints>` option.
  9. ``ExclusionConstraint``
  10. =======================
  11. .. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None)
  12. Creates an exclusion constraint in the database. Internally, PostgreSQL
  13. implements exclusion constraints using indexes. The default index type is
  14. `GiST <https://www.postgresql.org/docs/current/gist.html>`_. To use them,
  15. you need to activate the `btree_gist extension
  16. <https://www.postgresql.org/docs/current/btree-gist.html>`_ on PostgreSQL.
  17. You can install it using the
  18. :class:`~django.contrib.postgres.operations.BtreeGistExtension` migration
  19. operation.
  20. If you attempt to insert a new row that conflicts with an existing row, an
  21. :exc:`~django.db.IntegrityError` is raised. Similarly, when update
  22. conflicts with an existing row.
  23. Exclusion constraints are checked during the :ref:`model validation
  24. <validating-objects>`.
  25. .. versionchanged:: 4.1
  26. In older versions, exclusion constraints were not checked during model
  27. validation.
  28. ``name``
  29. --------
  30. .. attribute:: ExclusionConstraint.name
  31. See :attr:`.BaseConstraint.name`.
  32. ``expressions``
  33. ---------------
  34. .. attribute:: ExclusionConstraint.expressions
  35. An iterable of 2-tuples. The first element is an expression or string. The
  36. second element is an SQL operator represented as a string. To avoid typos, you
  37. may use :class:`~django.contrib.postgres.fields.RangeOperators` which maps the
  38. operators with strings. For example::
  39. expressions=[
  40. ('timespan', RangeOperators.ADJACENT_TO),
  41. (F('room'), RangeOperators.EQUAL),
  42. ]
  43. .. admonition:: Restrictions on operators.
  44. Only commutative operators can be used in exclusion constraints.
  45. The :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` expression can
  46. be used to specify a custom `operator class`_ for the constraint expressions.
  47. For example::
  48. expressions=[
  49. (OpClass('circle', name='circle_ops'), RangeOperators.OVERLAPS),
  50. ]
  51. creates an exclusion constraint on ``circle`` using ``circle_ops``.
  52. .. versionchanged:: 4.1
  53. Support for the ``OpClass()`` expression was added.
  54. .. _operator class: https://www.postgresql.org/docs/current/indexes-opclass.html
  55. ``index_type``
  56. --------------
  57. .. attribute:: ExclusionConstraint.index_type
  58. The index type of the constraint. Accepted values are ``GIST`` or ``SPGIST``.
  59. Matching is case insensitive. If not provided, the default index type is
  60. ``GIST``.
  61. ``condition``
  62. -------------
  63. .. attribute:: ExclusionConstraint.condition
  64. A :class:`~django.db.models.Q` object that specifies the condition to restrict
  65. a constraint to a subset of rows. For example,
  66. ``condition=Q(cancelled=False)``.
  67. These conditions have the same database restrictions as
  68. :attr:`django.db.models.Index.condition`.
  69. ``deferrable``
  70. --------------
  71. .. attribute:: ExclusionConstraint.deferrable
  72. Set this parameter to create a deferrable exclusion constraint. Accepted values
  73. are ``Deferrable.DEFERRED`` or ``Deferrable.IMMEDIATE``. For example::
  74. from django.contrib.postgres.constraints import ExclusionConstraint
  75. from django.contrib.postgres.fields import RangeOperators
  76. from django.db.models import Deferrable
  77. ExclusionConstraint(
  78. name='exclude_overlapping_deferred',
  79. expressions=[
  80. ('timespan', RangeOperators.OVERLAPS),
  81. ],
  82. deferrable=Deferrable.DEFERRED,
  83. )
  84. By default constraints are not deferred. A deferred constraint will not be
  85. enforced until the end of the transaction. An immediate constraint will be
  86. enforced immediately after every command.
  87. .. warning::
  88. Deferred exclusion constraints may lead to a `performance penalty
  89. <https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4>`_.
  90. ``include``
  91. -----------
  92. .. attribute:: ExclusionConstraint.include
  93. A list or tuple of the names of the fields to be included in the covering
  94. exclusion constraint as non-key columns. This allows index-only scans to be
  95. used for queries that select only included fields
  96. (:attr:`~ExclusionConstraint.include`) and filter only by indexed fields
  97. (:attr:`~ExclusionConstraint.expressions`).
  98. ``include`` is supported for GiST indexes. PostgreSQL 14+ also supports
  99. ``include`` for SP-GiST indexes.
  100. .. versionchanged:: 4.1
  101. Support for covering exclusion constraints using SP-GiST indexes on
  102. PostgreSQL 14+ was added.
  103. ``opclasses``
  104. -------------
  105. .. attribute:: ExclusionConstraint.opclasses
  106. The names of the `PostgreSQL operator classes
  107. <https://www.postgresql.org/docs/current/indexes-opclass.html>`_ to use for
  108. this constraint. If you require a custom operator class, you must provide one
  109. for each expression in the constraint.
  110. For example::
  111. ExclusionConstraint(
  112. name='exclude_overlapping_opclasses',
  113. expressions=[('circle', RangeOperators.OVERLAPS)],
  114. opclasses=['circle_ops'],
  115. )
  116. creates an exclusion constraint on ``circle`` using ``circle_ops``.
  117. .. deprecated:: 4.1
  118. The ``opclasses`` parameter is deprecated in favor of using
  119. :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` in
  120. :attr:`~ExclusionConstraint.expressions`.
  121. ``violation_error_message``
  122. ---------------------------
  123. .. versionadded:: 4.1
  124. The error message used when ``ValidationError`` is raised during
  125. :ref:`model validation <validating-objects>`. Defaults to
  126. :attr:`.BaseConstraint.violation_error_message`.
  127. Examples
  128. --------
  129. The following example restricts overlapping reservations in the same room, not
  130. taking canceled reservations into account::
  131. from django.contrib.postgres.constraints import ExclusionConstraint
  132. from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
  133. from django.db import models
  134. from django.db.models import Q
  135. class Room(models.Model):
  136. number = models.IntegerField()
  137. class Reservation(models.Model):
  138. room = models.ForeignKey('Room', on_delete=models.CASCADE)
  139. timespan = DateTimeRangeField()
  140. cancelled = models.BooleanField(default=False)
  141. class Meta:
  142. constraints = [
  143. ExclusionConstraint(
  144. name='exclude_overlapping_reservations',
  145. expressions=[
  146. ('timespan', RangeOperators.OVERLAPS),
  147. ('room', RangeOperators.EQUAL),
  148. ],
  149. condition=Q(cancelled=False),
  150. ),
  151. ]
  152. In case your model defines a range using two fields, instead of the native
  153. PostgreSQL range types, you should write an expression that uses the equivalent
  154. function (e.g. ``TsTzRange()``), and use the delimiters for the field. Most
  155. often, the delimiters will be ``'[)'``, meaning that the lower bound is
  156. inclusive and the upper bound is exclusive. You may use the
  157. :class:`~django.contrib.postgres.fields.RangeBoundary` that provides an
  158. expression mapping for the `range boundaries <https://www.postgresql.org/docs/
  159. current/rangetypes.html#RANGETYPES-INCLUSIVITY>`_. For example::
  160. from django.contrib.postgres.constraints import ExclusionConstraint
  161. from django.contrib.postgres.fields import (
  162. DateTimeRangeField,
  163. RangeBoundary,
  164. RangeOperators,
  165. )
  166. from django.db import models
  167. from django.db.models import Func, Q
  168. class TsTzRange(Func):
  169. function = 'TSTZRANGE'
  170. output_field = DateTimeRangeField()
  171. class Reservation(models.Model):
  172. room = models.ForeignKey('Room', on_delete=models.CASCADE)
  173. start = models.DateTimeField()
  174. end = models.DateTimeField()
  175. cancelled = models.BooleanField(default=False)
  176. class Meta:
  177. constraints = [
  178. ExclusionConstraint(
  179. name='exclude_overlapping_reservations',
  180. expressions=(
  181. (TsTzRange('start', 'end', RangeBoundary()), RangeOperators.OVERLAPS),
  182. ('room', RangeOperators.EQUAL),
  183. ),
  184. condition=Q(cancelled=False),
  185. ),
  186. ]