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)[原始碼]

在資料庫中建立排除約束。在內部,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_opscircle 上建立排除約束。

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)。

include 支援 GiST 索引。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),
            ),
        ]
返回頂部