Ограничения базы данных, специфичные для PostgreSQL

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

ExclusionConstraint

Новое в Django 3.0.
classExclusionConstraint ( * , имя , выражения , index_type = None , condition = None , deferrable = None )

Создает ограничение исключения в базе данных. Внутри PostgreSQL реализует ограничения исключения с помощью индексов. Тип индекса по умолчанию - GiST . Чтобы использовать их, вам необходимо включить расширение btree_gist в PostgreSQL. Вы можете установить расширение с помощью операции миграции BtreeGistExtension .

Если вы попытаетесь вставить новую строку, которая конфликтует с существующей строкой, произойдет ошибка IntegrityError . Точно так же, когда возникает конфликт во время обновления.

name

ExclusionConstraint.name

Имя ограничения.

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
Новое в 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,
)

По умолчанию ограничения не откладываются. Отложенное ограничение не будет применяться до конца транзакции. Немедленное ограничение будет применяться сразу после каждой команды.

Предупреждение

Ограничения отложенного исключения могут привести к снижению производительности .

Примеры

В следующем примере показано, как избежать дублирования бронирований для одной и той же комнаты без учета отмененных бронирований.

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),
            ),
        ]

Copyright ©2020 All rights reserved