constraints.txt 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. .. versionadded:: 3.0
  12. .. class:: ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None)
  13. Creates an exclusion constraint in the database. Internally, PostgreSQL
  14. implements exclusion constraints using indexes. The default index type is
  15. `GiST <https://www.postgresql.org/docs/current/gist.html>`_. To use them,
  16. you need to activate the `btree_gist extension
  17. <https://www.postgresql.org/docs/current/btree-gist.html>`_ on PostgreSQL.
  18. You can install it using the
  19. :class:`~django.contrib.postgres.operations.BtreeGistExtension` migration
  20. operation.
  21. If you attempt to insert a new row that conflicts with an existing row, an
  22. :exc:`~django.db.IntegrityError` is raised. Similarly, when update
  23. conflicts with an existing row.
  24. ``name``
  25. --------
  26. .. attribute:: ExclusionConstraint.name
  27. The name of the constraint.
  28. ``expressions``
  29. ---------------
  30. .. attribute:: ExclusionConstraint.expressions
  31. An iterable of 2-tuples. The first element is an expression or string. The
  32. second element is an SQL operator represented as a string. To avoid typos, you
  33. may use :class:`~django.contrib.postgres.fields.RangeOperators` which maps the
  34. operators with strings. For example::
  35. expressions=[
  36. ('timespan', RangeOperators.ADJACENT_TO),
  37. (F('room'), RangeOperators.EQUAL),
  38. ]
  39. .. admonition:: Restrictions on operators.
  40. Only commutative operators can be used in exclusion constraints.
  41. ``index_type``
  42. --------------
  43. .. attribute:: ExclusionConstraint.index_type
  44. The index type of the constraint. Accepted values are ``GIST`` or ``SPGIST``.
  45. Matching is case insensitive. If not provided, the default index type is
  46. ``GIST``.
  47. ``condition``
  48. -------------
  49. .. attribute:: ExclusionConstraint.condition
  50. A :class:`~django.db.models.Q` object that specifies the condition to restrict
  51. a constraint to a subset of rows. For example,
  52. ``condition=Q(cancelled=False)``.
  53. These conditions have the same database restrictions as
  54. :attr:`django.db.models.Index.condition`.
  55. ``deferrable``
  56. --------------
  57. .. attribute:: ExclusionConstraint.deferrable
  58. .. versionadded:: 3.1
  59. Set this parameter to create a deferrable exclusion constraint. Accepted values
  60. are ``Deferrable.DEFERRED`` or ``Deferrable.IMMEDIATE``. For example::
  61. from django.contrib.postgres.constraints import ExclusionConstraint
  62. from django.contrib.postgres.fields import RangeOperators
  63. from django.db.models import Deferrable
  64. ExclusionConstraint(
  65. name='exclude_overlapping_deferred',
  66. expressions=[
  67. ('timespan', RangeOperators.OVERLAPS),
  68. ],
  69. deferrable=Deferrable.DEFERRED,
  70. )
  71. By default constraints are not deferred. A deferred constraint will not be
  72. enforced until the end of the transaction. An immediate constraint will be
  73. enforced immediately after every command.
  74. .. warning::
  75. Deferred exclusion constraints may lead to a `performance penalty
  76. <https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4>`_.
  77. Examples
  78. --------
  79. The following example restricts overlapping reservations in the same room, not
  80. taking canceled reservations into account::
  81. from django.contrib.postgres.constraints import ExclusionConstraint
  82. from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
  83. from django.db import models
  84. from django.db.models import Q
  85. class Room(models.Model):
  86. number = models.IntegerField()
  87. class Reservation(models.Model):
  88. room = models.ForeignKey('Room', on_delete=models.CASCADE)
  89. timespan = DateTimeRangeField()
  90. cancelled = models.BooleanField(default=False)
  91. class Meta:
  92. constraints = [
  93. ExclusionConstraint(
  94. name='exclude_overlapping_reservations',
  95. expressions=[
  96. ('timespan', RangeOperators.OVERLAPS),
  97. ('room', RangeOperators.EQUAL),
  98. ],
  99. condition=Q(cancelled=False),
  100. ),
  101. ]
  102. In case your model defines a range using two fields, instead of the native
  103. PostgreSQL range types, you should write an expression that uses the equivalent
  104. function (e.g. ``TsTzRange()``), and use the delimiters for the field. Most
  105. often, the delimiters will be ``'[)'``, meaning that the lower bound is
  106. inclusive and the upper bound is exclusive. You may use the
  107. :class:`~django.contrib.postgres.fields.RangeBoundary` that provides an
  108. expression mapping for the `range boundaries <https://www.postgresql.org/docs/
  109. current/rangetypes.html#RANGETYPES-INCLUSIVITY>`_. For example::
  110. from django.contrib.postgres.constraints import ExclusionConstraint
  111. from django.contrib.postgres.fields import (
  112. DateTimeRangeField,
  113. RangeBoundary,
  114. RangeOperators,
  115. )
  116. from django.db import models
  117. from django.db.models import Func, Q
  118. class TsTzRange(Func):
  119. function = 'TSTZRANGE'
  120. output_field = DateTimeRangeField()
  121. class Reservation(models.Model):
  122. room = models.ForeignKey('Room', on_delete=models.CASCADE)
  123. start = models.DateTimeField()
  124. end = models.DateTimeField()
  125. cancelled = models.BooleanField(default=False)
  126. class Meta:
  127. constraints = [
  128. ExclusionConstraint(
  129. name='exclude_overlapping_reservations',
  130. expressions=(
  131. (TsTzRange('start', 'end', RangeBoundary()), RangeOperators.OVERLAPS),
  132. ('room', RangeOperators.EQUAL),
  133. ),
  134. condition=Q(cancelled=False),
  135. ),
  136. ]