constraints.txt 7.1 KB

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