PostgreSQL 特有数据库约束Link to this heading

PostgreSQL 支持额外的数据完整性约束,可从 django.contrib.postgres.cracks 模块中获得。它们被添加到模型 Meta.constraints 选项中。

ExclusionConstraintLink to this heading

class ExclusionConstraint(*, name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None)Link to this definition

在数据库中创建一个排除约束。在内部,PostgreSQL 使用索引来实现排除约束的功能。默认的索引类型是 GiST 。要使用它们,你需要激活 PostgreSQL 上的 btree_gist 扩展 。你可以使用 BtreeGistExtension 迁移操作来安装它。

如果你试图插入一条新的记录,而这条记录与现有的记录发生冲突,就会引发一个 IntegrityError。同样,当更新与现有的记录冲突时,也会发出 IntegrityError

排除约束在 模型验证 期间进行检查。

nameLink to this heading

ExclusionConstraint.nameLink to this definition

请参阅 BaseConstraint.name

expressionsLink to this heading

ExclusionConstraint.expressionsLink to this definition

一个二元元组的迭代。第一个元素是一个表达式或字符串。第二个元素是一个用字符串表示的 SQL 操作符。为了避免错别字,你可以使用 RangeOperators 将操作符与字符串进行映射。例如:

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

OpClass() 表达式可用于为约束表达式指定自定义的 operator class。例如:

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

使用 circle_opscircle 上创建一个排除约束。

index_typeLink to this heading

ExclusionConstraint.index_typeLink to this definition

限制因素的索引类型。接受的值是 GISTSPGIST。匹配是不分大小写的。如果没有提供,默认索引类型是 GIST

conditionLink to this heading

ExclusionConstraint.conditionLink to this definition

一个 Q 对象,用于指定将约束条件限制在行的子集上。例如,condition=Q(cancelled=False)

这些条件与 django.db.models.Index.condition 具有相同的数据库限制。

deferrableLink to this heading

ExclusionConstraint.deferrableLink to this definition

设置这一参数,可创建一个可推迟的排除限制。接受的值是 Deferrable.DEFERREDDeferrable.IMMEDIATE。例如:

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

默认情况下,约束条件是不推迟的。推迟的约束条件在事务结束前不会被强制执行。即时约束将在每条命令后立即执行。

includeLink to this heading

ExclusionConstraint.includeLink to this definition

一个包含在覆盖排除约束中作为非键列的字段名称的列表或元组。这允许只用索引扫描,用于只选择包含的字段(include)和只通过索引字段过滤(expressions)的查询。

对于 GiST 索引,支持 include。PostgreSQL 14+ 还支持 SP-GiST 索引的 include

opclassesLink to this heading

ExclusionConstraint.opclassesLink to this definition

用于此约束的 PostgreSQL 运算符类的名称 。如果你需要一个自定义的运算符类,你必须为约束中的每个表达式提供一个。

例子:

Code
ExclusionConstraint(
    name="exclude_overlapping_opclasses",
    expressions=[("circle", RangeOperators.OVERLAPS)],
    opclasses=["circle_ops"],
)

使用 circle_opscircle 上创建一个排除约束。

violation_error_messageLink to this heading

模型验证 期间引发 ValidationError 时使用的错误消息。默认为 BaseConstraint.violation_error_message

例子Link to this heading

以下例子限制同一房间中的重复预订,而不考虑取消的预订:

Code
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 提供了一个表达式映射。例如:

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