Ограничения конкретной базы данных PostgreSQL¶
PostgreSQL поддерживает дополнительные ограничения целостности данных, доступные в модуле django.contrib.postgres.constraints. Они добавляются в параметр модели Meta.constraints.
Ограничение исключения¶
- class ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=())¶
Создает ограничение исключения в базе данных. Внутри PostgreSQL ограничения исключения реализуются с помощью индексов. Тип индекса по умолчанию — GiST. Чтобы использовать их, вам необходимо активировать расширение btree_gist в PostgreSQL. Вы можете установить его с помощью операции миграции
BtreeGistExtension.Если вы попытаетесь вставить новую строку, которая конфликтует с существующей, выдается ошибка
IntegrityError. Аналогично, когда обновление конфликтует с существующей строкой.
name¶
- ExclusionConstraint.name¶
The name of the constraint.
expression¶
- ExclusionConstraint.expressions¶
Итерация двух кортежей. Первый элемент — это выражение или строка. Второй элемент — это оператор SQL, представленный в виде строки. Чтобы избежать опечаток, вы можете использовать RangeOperators, который сопоставляет операторы со строками. Например:
expressions=[
('timespan', RangeOperators.ADJACENT_TO),
(F('room'), RangeOperators.EQUAL),
]
Ограничения на операторов.
В ограничениях исключения можно использовать только коммутативные операторы.
index_type¶
- ExclusionConstraint.index_type¶
Тип индекса ограничения. Допустимые значения: GIST или SPGIST. Сопоставление нечувствительно к регистру. Если он не указан, тип индекса по умолчанию — GIST.
условие¶
- ExclusionConstraint.condition¶
Объект Q, задающий условие ограничения ограничения подмножеством строк. Например, condition=Q(cancelled=False).
Эти условия имеют те же ограничения базы данных, что и django.db.models.Index.condition.
отсрочка¶
- 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,
)
По умолчанию ограничения не откладываются. Отложенное ограничение не будет применяться до завершения транзакции. Немедленное ограничение будет применяться сразу после каждой команды.
Предупреждение
Ограничения отложенного исключения могут привести к «снижению производительности <https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4>».
include¶
- ExclusionConstraint.include¶
Список или кортеж имен полей, которые будут включены в ограничение исключения покрытия как неключевые столбцы. Это позволяет использовать сканирование только по индексу для запросов, которые выбирают только включенные поля (include) и фильтруют только по индексированным полям (expressions).
include is supported only for GiST indexes on PostgreSQL 12+.
опклассы¶
- ExclusionConstraint.opclasses¶
The names of the PostgreSQL operator classes to use for this constraint. If you require a custom operator class, you must provide one for each expression in the constraint.
Например:
ExclusionConstraint(
name='exclude_overlapping_opclasses',
expressions=[('circle', RangeOperators.OVERLAPS)],
opclasses=['circle_ops'],
)
создает ограничение исключения для «круга», используя «circle_ops».
Примеры¶
В следующем примере ограничиваются перекрывающиеся бронирования в одном номере, не принимая во внимание отмененные бронирования:
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, который обеспечивает отображение выражений для границ диапазона. Например:
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),
),
]