constraints.txt 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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, violation_error_code=None, 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. ``name``
  26. --------
  27. .. attribute:: ExclusionConstraint.name
  28. See :attr:`.BaseConstraint.name`.
  29. ``expressions``
  30. ---------------
  31. .. attribute:: ExclusionConstraint.expressions
  32. An iterable of 2-tuples. The first element is an expression or string. The
  33. second element is an SQL operator represented as a string. To avoid typos, you
  34. may use :class:`~django.contrib.postgres.fields.RangeOperators` which maps the
  35. operators with strings. For example::
  36. expressions = [
  37. ("timespan", RangeOperators.ADJACENT_TO),
  38. (F("room"), RangeOperators.EQUAL),
  39. ]
  40. .. admonition:: Restrictions on operators.
  41. Only commutative operators can be used in exclusion constraints.
  42. The :class:`OpClass() <django.contrib.postgres.indexes.OpClass>` expression can
  43. be used to specify a custom `operator class`_ for the constraint expressions.
  44. For example::
  45. expressions = [
  46. (OpClass("circle", name="circle_ops"), RangeOperators.OVERLAPS),
  47. ]
  48. creates an exclusion constraint on ``circle`` using ``circle_ops``.
  49. .. _operator class: https://www.postgresql.org/docs/current/indexes-opclass.html
  50. ``index_type``
  51. --------------
  52. .. attribute:: ExclusionConstraint.index_type
  53. The index type of the constraint. Accepted values are ``GIST`` or ``SPGIST``.
  54. Matching is case insensitive. If not provided, the default index type is
  55. ``GIST``.
  56. ``condition``
  57. -------------
  58. .. attribute:: ExclusionConstraint.condition
  59. A :class:`~django.db.models.Q` object that specifies the condition to restrict
  60. a constraint to a subset of rows. For example,
  61. ``condition=Q(cancelled=False)``.
  62. These conditions have the same database restrictions as
  63. :attr:`django.db.models.Index.condition`.
  64. ``deferrable``
  65. --------------
  66. .. attribute:: ExclusionConstraint.deferrable
  67. Set this parameter to create a deferrable exclusion constraint. Accepted values
  68. are ``Deferrable.DEFERRED`` or ``Deferrable.IMMEDIATE``. For example::
  69. from django.contrib.postgres.constraints import ExclusionConstraint
  70. from django.contrib.postgres.fields import RangeOperators
  71. from django.db.models import Deferrable
  72. ExclusionConstraint(
  73. name="exclude_overlapping_deferred",
  74. expressions=[
  75. ("timespan", RangeOperators.OVERLAPS),
  76. ],
  77. deferrable=Deferrable.DEFERRED,
  78. )
  79. By default constraints are not deferred. A deferred constraint will not be
  80. enforced until the end of the transaction. An immediate constraint will be
  81. enforced immediately after every command.
  82. .. warning::
  83. Deferred exclusion constraints may lead to a `performance penalty
  84. <https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4>`_.
  85. ``include``
  86. -----------
  87. .. attribute:: ExclusionConstraint.include
  88. A list or tuple of the names of the fields to be included in the covering
  89. exclusion constraint as non-key columns. This allows index-only scans to be
  90. used for queries that select only included fields
  91. (:attr:`~ExclusionConstraint.include`) and filter only by indexed fields
  92. (:attr:`~ExclusionConstraint.expressions`).
  93. ``include`` is supported for GiST indexes. PostgreSQL 14+ also supports
  94. ``include`` for SP-GiST indexes.
  95. ``violation_error_code``
  96. ------------------------
  97. .. attribute:: ExclusionConstraint.violation_error_code
  98. The error code used when ``ValidationError`` is raised during
  99. :ref:`model validation <validating-objects>`. Defaults to ``None``.
  100. ``violation_error_message``
  101. ---------------------------
  102. The error message used when ``ValidationError`` is raised during
  103. :ref:`model validation <validating-objects>`. Defaults to
  104. :attr:`.BaseConstraint.violation_error_message`.
  105. Examples
  106. --------
  107. The following example restricts overlapping reservations in the same room, not
  108. taking canceled reservations into account::
  109. from django.contrib.postgres.constraints import ExclusionConstraint
  110. from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
  111. from django.db import models
  112. from django.db.models import Q
  113. class Room(models.Model):
  114. number = models.IntegerField()
  115. class Reservation(models.Model):
  116. room = models.ForeignKey("Room", on_delete=models.CASCADE)
  117. timespan = DateTimeRangeField()
  118. cancelled = models.BooleanField(default=False)
  119. class Meta:
  120. constraints = [
  121. ExclusionConstraint(
  122. name="exclude_overlapping_reservations",
  123. expressions=[
  124. ("timespan", RangeOperators.OVERLAPS),
  125. ("room", RangeOperators.EQUAL),
  126. ],
  127. condition=Q(cancelled=False),
  128. ),
  129. ]
  130. In case your model defines a range using two fields, instead of the native
  131. PostgreSQL range types, you should write an expression that uses the equivalent
  132. function (e.g. ``TsTzRange()``), and use the delimiters for the field. Most
  133. often, the delimiters will be ``'[)'``, meaning that the lower bound is
  134. inclusive and the upper bound is exclusive. You may use the
  135. :class:`~django.contrib.postgres.fields.RangeBoundary` that provides an
  136. expression mapping for the `range boundaries <https://www.postgresql.org/docs/
  137. current/rangetypes.html#RANGETYPES-INCLUSIVITY>`_. For example::
  138. from django.contrib.postgres.constraints import ExclusionConstraint
  139. from django.contrib.postgres.fields import (
  140. DateTimeRangeField,
  141. RangeBoundary,
  142. RangeOperators,
  143. )
  144. from django.db import models
  145. from django.db.models import Func, Q
  146. class TsTzRange(Func):
  147. function = "TSTZRANGE"
  148. output_field = DateTimeRangeField()
  149. class Reservation(models.Model):
  150. room = models.ForeignKey("Room", on_delete=models.CASCADE)
  151. start = models.DateTimeField()
  152. end = models.DateTimeField()
  153. cancelled = models.BooleanField(default=False)
  154. class Meta:
  155. constraints = [
  156. ExclusionConstraint(
  157. name="exclude_overlapping_reservations",
  158. expressions=[
  159. (
  160. TsTzRange("start", "end", RangeBoundary()),
  161. RangeOperators.OVERLAPS,
  162. ),
  163. ("room", RangeOperators.EQUAL),
  164. ],
  165. condition=Q(cancelled=False),
  166. ),
  167. ]