---
title: "PostgreSQL 特有数据库约束"
version: 4.2
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/postgres/constraints/
canonical: https://djangodocs.dev/zh-hans/4.2/ref/contrib/postgres/constraints/
---
# PostgreSQL 特有数据库约束

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

## `ExclusionConstraint`

#### `class ExclusionConstraint(* (Keyword-only parameters separator (PEP 3102)), name, expressions, index_type=None, condition=None, deferrable=None, include=None, opclasses=(), violation_error_message=None)`

在数据库中创建一个排除约束。在内部，PostgreSQL 使用索引来实现排除约束的功能。默认的索引类型是 [GiST](https://www.postgresql.org/docs/current/gist.html) 。要使用它们，你需要激活 PostgreSQL 上的 [btree\_gist 扩展](https://www.postgresql.org/docs/current/btree-gist.html) 。你可以使用 [`BtreeGistExtension`](/zh-hans/4.2/ref/contrib/postgres/operations/#django.contrib.postgres.operations.BtreeGistExtension) 迁移操作来安装它。

如果你试图插入一条新的记录，而这条记录与现有的记录发生冲突，就会引发一个 [`IntegrityError`](/zh-hans/4.2/ref/exceptions/#django.db.IntegrityError)。同样，当更新与现有的记录冲突时，也会发出 [`IntegrityError`](/zh-hans/4.2/ref/exceptions/#django.db.IntegrityError)。

排除约束在 [模型验证](/zh-hans/4.2/ref/models/instances/#validating-objects) 期间进行检查。

> **Changed in Django 4.1**
>
> 在旧版本中，排除约束在模型验证期间不会被检查。

### `name`

#### `ExclusionConstraint.name`

请参阅 [`BaseConstraint.name`](/zh-hans/4.2/ref/models/constraints/#django.db.models.BaseConstraint.name)。

### `expressions`

#### `ExclusionConstraint.expressions`

一个二元元组的迭代。第一个元素是一个表达式或字符串。第二个元素是一个用字符串表示的 SQL 操作符。为了避免错别字，你可以使用 [`RangeOperators`](/zh-hans/4.2/ref/contrib/postgres/fields/#django.contrib.postgres.fields.RangeOperators) 将操作符与字符串进行映射。例如：

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

> **对操作的限制。**
>
> 只有交换运算符才能用于排除约束。

[`OpClass()`](/zh-hans/4.2/ref/contrib/postgres/indexes/#django.contrib.postgres.indexes.OpClass) 表达式可用于为约束表达式指定自定义的 [operator class](https://www.postgresql.org/docs/current/indexes-opclass.html)。例如：

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

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

> **Changed in Django 4.1**
>
> 已添加对 `OpClass()` 表达式的支持。

### `index_type`

#### `ExclusionConstraint.index_type`

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

### `condition`

#### `ExclusionConstraint.condition`

一个 [`Q`](/zh-hans/4.2/ref/models/querysets/#django.db.models.Q) 对象，用于指定将约束条件限制在行的子集上。例如，`condition=Q(cancelled=False)`。

这些条件与 [`django.db.models.Index.condition`](/zh-hans/4.2/ref/models/indexes/#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**
>
> 延迟排除约束可能导致 [性能惩罚](https://www.postgresql.org/docs/current/sql-createtable.html#id-1.9.3.85.9.4) 。

### `include`

#### `ExclusionConstraint.include`

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

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

> **Changed in Django 4.1**
>
> 已添加对 PostgreSQL 14+ 上使用 SP-GiST 索引进行覆盖排除约束的支持。

### `opclasses`

#### `ExclusionConstraint.opclasses`

用于此约束的 [PostgreSQL 运算符类的名称](https://www.postgresql.org/docs/current/indexes-opclass.html) 。如果你需要一个自定义的运算符类，你必须为约束中的每个表达式提供一个。

例子：

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

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

> **Deprecated since Django 4.1**
>
> Deprecated since version 4.1: `opclasses` 参数已弃用，建议在 [`expressions`](#django.contrib.postgres.constraints.ExclusionConstraint.expressions) 中使用 [`OpClass()`](/zh-hans/4.2/ref/contrib/postgres/indexes/#django.contrib.postgres.indexes.OpClass)。

### `violation_error_message`

> **New in Django 4.1**

在 [模型验证](/zh-hans/4.2/ref/models/instances/#validating-objects) 期间引发 `ValidationError` 时使用的错误消息。默认为 [`BaseConstraint.violation_error_message`](/zh-hans/4.2/ref/models/constraints/#django.db.models.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`](/zh-hans/4.2/ref/contrib/postgres/fields/#django.contrib.postgres.fields.RangeBoundary)，它为 [range boundaries](https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-INCLUSIVITY) 提供了一个表达式映射。例如：

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