Ограничения базы данных, специфичные для PostgreSQL¶
PostgreSQL поддерживает дополнительные ограничения целостности данных, доступные из модуля django.contrib.postgres.constraints
. Они добавляются в опции модели Meta.constraints
.
ExclusionConstraint
¶
-
class
ExclusionConstraint
(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None)[исходный код]¶ Создает исключающее ограничение в базе данных. Внутри PostgreSQL ограничения исключения реализуются с помощью индексов. Тип индекса по умолчанию - GiST. Чтобы использовать их, необходимо активировать btree_gist extension на PostgreSQL. Вы можете установить его с помощью операции миграции
BtreeGistExtension
.Если вы пытаетесь вставить новый ряд, который конфликтует с существующим рядом, возникает ошибка
IntegrityError
. Аналогично, если обновление конфликтует с существующим рядом.Ограничения исключения проверяются во время model validation.
Changed in Django Development version:В старых версиях ограничения на исключение не проверялись во время валидации модели.
expressions
¶
-
ExclusionConstraint.
expressions
¶
Итерабельность из двух кортежей. Первый элемент - выражение или строка. Второй элемент - оператор SQL, представленный в виде строки. Чтобы избежать опечаток, вы можете использовать RangeOperators
, который сопоставляет операторы со строками. Например:
expressions=[
('timespan', RangeOperators.ADJACENT_TO),
(F('room'), RangeOperators.EQUAL),
]
Ограничения для операторов.
В ограничениях исключения могут использоваться только коммутативные операторы.
Выражение OpClass()
можно использовать для задания пользовательского operator class для выражений ограничений. Например:
expressions=[
(OpClass('circle', name='circle_ops'), RangeOperators.OVERLAPS),
]
создает исключающее ограничение на circle
, используя circle_ops
.
Добавлена поддержка выражения OpClass()
.
index_type
¶
-
ExclusionConstraint.
index_type
¶
Тип индекса ограничения. Принимаются значения GIST
или SPGIST
. Соответствие нечувствительно к регистру. Если значение не указано, то по умолчанию используется тип индекса GIST
.
condition
¶
-
ExclusionConstraint.
condition
¶
Объект Q
, задающий условие для ограничения подмножества строк. Например, condition=Q(cancelled=False)
.
Эти условия имеют те же ограничения базы данных, что и django.db.models.Index.condition
.
deferrable
¶
-
ExclusionConstraint.
deferrable
¶
Установите этот параметр для создания откладываемого ограничения исключения. Принимаются значения Deferrable.DEFERRED
или Deferrable.IMMEDIATE
. Например:
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,
)
По умолчанию ограничения не откладываются. Отложенное ограничение не будет выполняться до конца транзакции. Немедленное ограничение будет выполняться сразу после каждой команды.
Предупреждение
Отложенные ограничения исключения могут привести к performance penalty.
include
¶
-
ExclusionConstraint.
include
¶
Список или кортеж имен полей, которые должны быть включены в охватывающее исключающее ограничение в качестве неключевых столбцов. Это позволяет использовать сканирование только по индексам для запросов, которые выбирают только включенные поля (include
) и фильтруют только по индексированным полям (expressions
).
include
is supported for GiST indexes. PostgreSQL 14+ also supports
include
for SP-GiST indexes.
Добавлена поддержка охватывающих ограничений исключения с использованием индексов SP-GiST в PostgreSQL 14+.
opclasses
¶
-
ExclusionConstraint.
opclasses
¶
Имена операторов PostgreSQL operator classes для использования в данном ограничении. Если вам требуется пользовательский класс оператора, вы должны предоставить его для каждого выражения в ограничении.
Например:
ExclusionConstraint(
name='exclude_overlapping_opclasses',
expressions=[('circle', RangeOperators.OVERLAPS)],
opclasses=['circle_ops'],
)
создает исключающее ограничение на circle
, используя circle_ops
.
Не рекомендуется, начиная с версии 4.1: Параметр opclasses
устарел в пользу использования OpClass()
в expressions
.
violation_error_message
¶
Сообщение об ошибке, используемое, когда ValidationError
возникает во время model validation. По умолчанию используется BaseConstraint.violation_error_message
.
Примеры:¶
Следующий пример ограничивает перекрывающиеся бронирования в одном номере, не принимая во внимание отмененные бронирования:
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),
),
]
В случае если ваша модель определяет диапазон с использованием двух полей, вместо стандартных типов диапазонов PostgreSQL, вы должны написать выражение, которое использует эквивалентную функцию (например, TsTzRange()
), и использовать разделители для поля. Чаще всего разделители будут '[)'
, что означает, что нижняя граница является включающей, а верхняя - исключающей. Вы можете использовать RangeBoundary
, который обеспечивает отображение выражения для range boundaries. Например:
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),
),
]