• 3.2
  • 5.0
  • 6.1
  • Версия документации: 3.1

Ограничения конкретной базы данных PostgreSQL

PostgreSQL поддерживает дополнительные ограничения целостности данных, доступные в модуле django.contrib.postgres.constraints. Они добавляются в параметр модели Meta.constraints.

Ограничение исключения

New in Django 3.0.
class ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None)

Создает ограничение исключения в базе данных. Внутри 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
New in Django 3.1.

Установите этот параметр, чтобы создать ограничение отложенного исключения. Допустимые значения: 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>».

Примеры

В следующем примере ограничиваются перекрывающиеся бронирования в одном номере, не принимая во внимание отмененные бронирования:

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),
            ),
        ]
Back to Top