Ограничения базы данных, специфичные для PostgreSQL ¶
PostgreSQL предлагает дополнительные ограничения целостности данных в модуле django.contrib.postgres.constraints
. Они добавлены в опции Meta.constraints
моделей.
ExclusionConstraint
¶
-
class
ExclusionConstraint
( * , имя , выражения , index_type = None , condition = None , deferrable = None ) ¶ Создает ограничение исключения в базе данных. Внутри PostgreSQL реализует ограничения исключения с помощью индексов. Тип индекса по умолчанию - GiST . Чтобы использовать их, вам необходимо включить расширение btree_gist в PostgreSQL. Вы можете установить расширение с помощью операции миграции
BtreeGistExtension
.Если вы попытаетесь вставить новую строку, которая конфликтует с существующей строкой, произойдет ошибка
IntegrityError
. Точно так же, когда возникает конфликт во время обновления.
expressions
¶
-
ExclusionConstraint.
expressions
¶
Итерация двоичных кортежей. Первый элемент - это выражение или строка. Второй элемент - это оператор SQL в виде строки. Чтобы избежать синтаксических ошибок, вы можете использовать RangeOperators
операторы, которые соответствуют строкам. например
expressions=[
('timespan', RangeOperators.ADJACENT_TO),
(F('room'), RangeOperators.EQUAL),
]
Ограничения на операторов.
В ограничениях исключения можно использовать только коммутативные операторы.
index_type
¶
-
ExclusionConstraint.
index_type
¶
Тип индекса ограничения. Допустимые значения: GIST
или SPGIST
. Матчи не учитывают регистр. В случае отсутствия тип индекса по умолчанию GIST
.
condition
¶
-
ExclusionConstraint.
condition
¶
Объект, Q
который указывает условие ограничения ограничения на подмножество строк. Например, condition=Q(annule=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,
)
По умолчанию ограничения не откладываются. Отложенное ограничение не будет применяться до конца транзакции. Немедленное ограничение будет применяться сразу после каждой команды.
Предупреждение
Ограничения отложенного исключения могут привести к снижению производительности .
Примеры ¶
В следующем примере показано, как избежать дублирования бронирований для одной и той же комнаты без учета отмененных бронирований.
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
which обеспечивает совпадение выражений для границ интервала . например
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),
),
]