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)[source]

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

如果您尝试插入与现有行冲突的新行,则会引发 IntegrityError。类似地,当更新与现有行冲突时。

模型验证 期间检查排他约束。

name

ExclusionConstraint.name

参见 BaseConstraint.name

expressions

ExclusionConstraint.expressions

一个 2 元组的可迭代对象。第一个元素是表达式或字符串。第二个元素是用字符串表示的 SQL 运算符。为了避免拼写错误,您可以使用 RangeOperators,它将运算符与字符串映射。例如

expressions = [
    ("timespan", RangeOperators.ADJACENT_TO),
    (F("room"), RangeOperators.EQUAL),
]

对运算符的限制。

排他约束中只能使用可交换运算符。

OpClass() 表达式可用于为约束表达式指定自定义 运算符类。例如

expressions = [
    (OpClass("circle", name="circle_ops"), RangeOperators.OVERLAPS),
]

circle 上使用 circle_ops 创建排他约束。

index_type

ExclusionConstraint.index_type

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

condition

ExclusionConstraint.condition

一个 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,
)

默认情况下,约束不会被延迟。延迟约束在事务结束之前不会被执行。立即约束将在每个命令之后立即执行。

警告

延迟的排他约束可能导致 性能下降

include

ExclusionConstraint.include

要包含在覆盖排他约束中作为非键列的字段名称列表或元组。这允许对仅选择包含字段 (include) 并仅根据索引字段 (expressions) 进行过滤的查询使用仅索引扫描。

GiST 索引支持 include。PostgreSQL 14+ 也支持 SP-GiST 索引的 include

violation_error_code

Django 5.0 中的新增功能。
ExclusionConstraint.violation_error_code

模型验证 期间引发 ValidationError 时使用的错误代码。默认为 None

violation_error_message

模型验证 期间引发 ValidationError 时使用的错误消息。默认为 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,它为 范围边界 提供表达式映射。例如

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),
            ),
        ]
返回顶部