PostgreSQL特定的数据库约束

PostgreSQL支持其他数据完整性约束,可从 django.contrib.postgres.constraints 模块。它们被添加到模型中 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元组的iterable。第一个元素是表达式或字符串。第二个元素是用字符串表示的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

New in Django 5.0.
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),
            ),
        ]