# Django 5.2.8 documentation

# PostgreSQL 特有数据库约束

PostgreSQL 支持额外的数据完整性约束，可从 `django.contrib.postgres.cracks` 模块中获得。它们被添加到模型 `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 。要使用它们，你需要激活 PostgreSQL 上的 btree_gist 扩展 。你可以使用 `BtreeGistExtension` 迁移操作来安装它。

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

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

### `name`

ExclusionConstraint.name

请参阅 `BaseConstraint.name`。

### `expressions`

ExclusionConstraint.expressions

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

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

对操作的限制。

只有交换运算符才能用于排除约束。

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

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

使用 `circle_ops` 在 `circle` 上创建一个排除约束。

### `index_type`

ExclusionConstraint.index_type

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

### `condition`

ExclusionConstraint.condition

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

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

### `deferrable`

ExclusionConstraint.deferrable

设置这一参数，可创建一个可推迟的排除限制。接受的值是 `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,
    )

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

Warning

延迟排除约束可能导致 性能惩罚 。

### `include`

ExclusionConstraint.include

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

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

### `violation_error_code`

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`，它为 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),
                ),
            ]