PostgreSQL 特有数据库约束

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

ExclusionConstraint

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

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

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

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

Changed in Django 4.1:

在旧版本中,排除约束在模型验证期间不会被检查。

name

ExclusionConstraint.name

请参阅 BaseConstraint.name

expressions

ExclusionConstraint.expressions

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

  1. expressions = [
  2. ("timespan", RangeOperators.ADJACENT_TO),
  3. (F("room"), RangeOperators.EQUAL),
  4. ]

对操作的限制。

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

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

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

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

Changed in Django 4.1:

已添加对 OpClass() 表达式的支持。

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。例如:

  1. from django.contrib.postgres.constraints import ExclusionConstraint
  2. from django.contrib.postgres.fields import RangeOperators
  3. from django.db.models import Deferrable
  4. ExclusionConstraint(
  5. name="exclude_overlapping_deferred",
  6. expressions=[
  7. ("timespan", RangeOperators.OVERLAPS),
  8. ],
  9. deferrable=Deferrable.DEFERRED,
  10. )

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

警告

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

include

ExclusionConstraint.include

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

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

Changed in Django 4.1:

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

opclasses

ExclusionConstraint.opclasses

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

例子:

  1. ExclusionConstraint(
  2. name="exclude_overlapping_opclasses",
  3. expressions=[("circle", RangeOperators.OVERLAPS)],
  4. opclasses=["circle_ops"],
  5. )

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

4.1 版后已移除: opclasses 参数已弃用,建议在 expressions 中使用 OpClass()

violation_error_message

New in Django 4.1.

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

例子

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

  1. from django.contrib.postgres.constraints import ExclusionConstraint
  2. from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
  3. from django.db import models
  4. from django.db.models import Q
  5. class Room(models.Model):
  6. number = models.IntegerField()
  7. class Reservation(models.Model):
  8. room = models.ForeignKey("Room", on_delete=models.CASCADE)
  9. timespan = DateTimeRangeField()
  10. cancelled = models.BooleanField(default=False)
  11. class Meta:
  12. constraints = [
  13. ExclusionConstraint(
  14. name="exclude_overlapping_reservations",
  15. expressions=[
  16. ("timespan", RangeOperators.OVERLAPS),
  17. ("room", RangeOperators.EQUAL),
  18. ],
  19. condition=Q(cancelled=False),
  20. ),
  21. ]

如果你的模型使用两个字段定义了一个范围,而不是原生的 PostgreSQL 范围类型,你应该写一个使用等价函数的表达式(例如 TsTzRange()),并使用字段的定界符。大多数情况下,定界符将是 '[)',这意味着下界是包含性的,上界是排他性的。你可以使用 RangeBoundary,它为 range boundaries 提供了一个表达式映射。例如:

  1. from django.contrib.postgres.constraints import ExclusionConstraint
  2. from django.contrib.postgres.fields import (
  3. DateTimeRangeField,
  4. RangeBoundary,
  5. RangeOperators,
  6. )
  7. from django.db import models
  8. from django.db.models import Func, Q
  9. class TsTzRange(Func):
  10. function = "TSTZRANGE"
  11. output_field = DateTimeRangeField()
  12. class Reservation(models.Model):
  13. room = models.ForeignKey("Room", on_delete=models.CASCADE)
  14. start = models.DateTimeField()
  15. end = models.DateTimeField()
  16. cancelled = models.BooleanField(default=False)
  17. class Meta:
  18. constraints = [
  19. ExclusionConstraint(
  20. name="exclude_overlapping_reservations",
  21. expressions=[
  22. (
  23. TsTzRange("start", "end", RangeBoundary()),
  24. RangeOperators.OVERLAPS,
  25. ),
  26. ("room", RangeOperators.EQUAL),
  27. ],
  28. condition=Q(cancelled=False),
  29. ),
  30. ]