PostgreSQL特定的数据库约束

PostgreSQL支持提供的其他数据完整性约束 django.contrib.postgres.constraints module.它们被添加到模型中 Meta.constraints 选择。

ExclusionConstraint

class ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, violation_error_code=None, violation_error_message=None)[源代码]

在数据库中创建排除约束。在内部,PostgreSQL使用索引实现排除约束。默认索引类型为 GiST .要使用它们,您需要激活 btree_gist extension 在PostgreSQL上。您可以使用 BtreeGistExtension 迁移操作。

如果您试图插入与现有行冲突的新行,则会出现 IntegrityError 被提出。同样,当更新与现有行冲突时。

期间检查排除约束。 model validation

name

ExclusionConstraint.name

看见 BaseConstraint.name

expressions

ExclusionConstraint.expressions

2-字节组的迭代对象。第一个元素是运算式或字符串。第二个元素是表示为字符串的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

index_type

ExclusionConstraint.index_type

约束的索引类型。接受的价值观是 GISTSPGIST .匹配不区分大小写。如果未提供,默认索引类型为 GIST

condition

ExclusionConstraint.condition

A Q 对象,指定将约束限制到行子集的条件。例如, condition=Q(cancelled=False)

这些条件具有与 django.db.models.Index.condition

deferrable

ExclusionConstraint.deferrable

设置此参数以创建可推迟的排除约束。接受的价值观是 Deferrable.DEFERREDDeferrable.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 GIST索引支持。PostgreSQL 14+还支持 include 用于SP-GIST索引。

violation_error_code

ExclusionConstraint.violation_error_code

出现以下情况时使用的错误代码 ValidationError 是在过程中引发的 model validation 。默认为 None

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