PostgreSQL 特定模型欄位

這些欄位都可從 django.contrib.postgres.fields 模組取得。

為這些欄位建立索引

IndexField.db_index 都會建立 B 樹索引,這在查詢複雜資料類型時並非特別有用。像是 GinIndexGistIndex 等索引更為適合,但索引的選擇取決於您正在使用的查詢。一般而言,GiST 可能適合範圍欄位HStoreField,而 GIN 可能對ArrayField 有幫助。

ArrayField

class ArrayField(base_field, size=None, **options)

用於儲存資料列表的欄位。可以使用大多數欄位類型,並且您可以將另一個欄位實例作為 base_field 傳遞。您也可以指定 sizeArrayField 可以巢狀使用以儲存多維陣列。

如果您給予欄位 default,請確保它是可呼叫的,例如 list (對於空預設值) 或返回列表的可呼叫函式。錯誤地使用 default=[] 會建立一個可變的預設值,該預設值在 ArrayField 的所有實例之間共享。

base_field

這是必要參數。

指定陣列的底層資料類型和行為。它應該是 Field 的子類別的實例。例如,它可以是 IntegerFieldCharField。允許使用大多數欄位類型,但不允許使用處理關聯資料的欄位 (ForeignKeyOneToOneFieldManyToManyField) 和檔案欄位 ( FileFieldImageField)。

可以巢狀使用陣列欄位 - 您可以指定 ArrayField 的實例作為 base_field。例如

from django.contrib.postgres.fields import ArrayField
from django.db import models


class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

資料庫和模型之間的值轉換、資料驗證和設定以及序列化都委派給底層基礎欄位。

size

這是可選參數。

如果傳遞此參數,則陣列將具有指定的最大大小。這將傳遞到資料庫,儘管 PostgreSQL 目前不強制執行此限制。

注意

當巢狀使用 ArrayField 時,無論您是否使用 size 參數,PostgreSQL 都要求陣列為矩形

from django.contrib.postgres.fields import ArrayField
from django.db import models


class Board(models.Model):
    pieces = ArrayField(ArrayField(models.IntegerField()))


# Valid
Board(
    pieces=[
        [2, 3],
        [2, 1],
    ]
)

# Not valid
Board(
    pieces=[
        [2, 3],
        [2],
    ]
)

如果需要不規則的形狀,則應該將底層欄位設為可為空,並且使用 None 填充值。

查詢 ArrayField

ArrayField 有許多自訂的查找和轉換。我們將使用以下範例模型

from django.contrib.postgres.fields import ArrayField
from django.db import models


class Post(models.Model):
    name = models.CharField(max_length=200)
    tags = ArrayField(models.CharField(max_length=200), blank=True)

    def __str__(self):
        return self.name

contains

contains 查找會在 ArrayField 上被覆寫。返回的物件將是那些傳遞的值是資料子集的物件。它使用 SQL 運算子 @>。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts"])
>>> Post.objects.create(name="Third post", tags=["tutorial", "django"])

>>> Post.objects.filter(tags__contains=["thoughts"])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__contains=["django"])
<QuerySet [<Post: First post>, <Post: Third post>]>

>>> Post.objects.filter(tags__contains=["django", "thoughts"])
<QuerySet [<Post: First post>]>

contained_by

這是 contains 查找的反向 - 返回的物件將是那些資料是傳遞的值的子集的物件。它使用 SQL 運算子 <@。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts"])
>>> Post.objects.create(name="Third post", tags=["tutorial", "django"])

>>> Post.objects.filter(tags__contained_by=["thoughts", "django"])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__contained_by=["thoughts", "django", "tutorial"])
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

overlap

返回資料與傳遞的值共享任何結果的物件。使用 SQL 運算子 &&。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts", "tutorial"])
>>> Post.objects.create(name="Third post", tags=["tutorial", "django"])

>>> Post.objects.filter(tags__overlap=["thoughts"])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__overlap=["thoughts", "tutorial"])
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

>>> Post.objects.filter(tags__overlap=Post.objects.values_list("tags"))
<QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>

len

返回陣列的長度。之後可用的查找是 IntegerField 可用的查找。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts"])

>>> Post.objects.filter(tags__len=1)
<QuerySet [<Post: Second post>]>

索引轉換

索引轉換會索引到陣列中。可以使用任何非負整數。如果它超過陣列的 size,則不會發生錯誤。轉換後可用的查找來自 base_field。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts"])

>>> Post.objects.filter(tags__0="thoughts")
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__1__iexact="Django")
<QuerySet [<Post: First post>]>

>>> Post.objects.filter(tags__276="javascript")
<QuerySet []>

注意

在撰寫原始 SQL 時,PostgreSQL 對於陣列欄位使用 1 為基礎的索引。但是這些索引和 slices 中使用的索引使用 0 為基礎的索引,以與 Python 保持一致。

切片轉換

切片轉換會取得陣列的切片。可以使用任意兩個非負整數,以單一下底線分隔。轉換後可用的查找不會變更。例如

>>> Post.objects.create(name="First post", tags=["thoughts", "django"])
>>> Post.objects.create(name="Second post", tags=["thoughts"])
>>> Post.objects.create(name="Third post", tags=["django", "python", "thoughts"])

>>> Post.objects.filter(tags__0_1=["thoughts"])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__0_2__contains=["thoughts"])
<QuerySet [<Post: First post>, <Post: Second post>]>

注意

在撰寫原始 SQL 時,PostgreSQL 對於陣列欄位使用 1 為基礎的索引。但是這些切片和 indexes 中使用的索引使用 0 為基礎的索引,以與 Python 保持一致。

具有索引和切片的多維陣列

當在多維陣列上使用索引和切片時,PostgreSQL 有一些相當深奧的行為。使用索引到達最終的底層資料總是會有效,但是大多數其他切片在資料庫層級的行為很奇怪,而且 Django 無法以邏輯一致的方式支援。

HStoreField

class HStoreField(**options)

用於儲存鍵值對的欄位。使用的 Python 資料類型為 dict。鍵必須是字串,值可以是字串或空值 (Python 中的 None)。

若要使用此欄位,您需要

  1. 在您的 INSTALLED_APPS 中新增 'django.contrib.postgres'

  2. 在 PostgreSQL 中設定 hstore 擴充功能

如果您跳過第一步,您會看到像 can't adapt type 'dict' 這樣的錯誤,如果您跳過第二步,您會看到 type "hstore" does not exist 這樣的錯誤。

注意

有時,可能需要要求或限制特定欄位的有效鍵。這可以使用 KeysValidator 來完成。

查詢 HStoreField

除了能夠依鍵查詢之外,還有許多自訂的查詢可供 HStoreField 使用。

我們將使用以下範例模型

from django.contrib.postgres.fields import HStoreField
from django.db import models


class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = HStoreField()

    def __str__(self):
        return self.name

鍵查詢

若要根據給定的鍵進行查詢,您可以使用該鍵作為查詢名稱

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie"})

>>> Dog.objects.filter(data__breed="collie")
<QuerySet [<Dog: Meg>]>

您可以在鍵查詢之後串連其他查詢

>>> Dog.objects.filter(data__breed__contains="l")
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

或使用 F() 運算式來註解鍵值。例如

>>> from django.db.models import F
>>> rufus = Dog.objects.annotate(breed=F("data__breed"))[0]
>>> rufus.breed
'labrador'

如果您要查詢的鍵與其他查詢的名稱衝突,則需要改用 hstorefield.contains 查詢。

注意

鍵轉換也可以與以下查詢串連: containsicontainsendswithiendswithiexactregexiregexstartswithistartswith 查詢。

警告

由於任何字串都可能是 hstore 值中的鍵,因此除了以下列出的查詢之外,任何查詢都會被解讀為鍵查詢。不會引發任何錯誤。請格外小心輸入錯誤,並始終檢查您的查詢是否如您預期般運作。

contains

contains 查詢在 HStoreField 上被覆寫。傳回的物件是那些給定的鍵值對 dict 都包含在欄位中的物件。它使用 SQL 運算子 @>。例如

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
>>> Dog.objects.create(name="Fred", data={})

>>> Dog.objects.filter(data__contains={"owner": "Bob"})
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

>>> Dog.objects.filter(data__contains={"breed": "collie"})
<QuerySet [<Dog: Meg>]>

contained_by

這是 contains 查詢的相反 - 傳回的物件將是那些物件上的鍵值對是傳遞的值的子集的物件。它使用 SQL 運算子 <@。例如

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador", "owner": "Bob"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})
>>> Dog.objects.create(name="Fred", data={})

>>> Dog.objects.filter(data__contained_by={"breed": "collie", "owner": "Bob"})
<QuerySet [<Dog: Meg>, <Dog: Fred>]>

>>> Dog.objects.filter(data__contained_by={"breed": "collie"})
<QuerySet [<Dog: Fred>]>

has_key

傳回資料中包含指定鍵的物件。使用 SQL 運算子 ?。例如

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})

>>> Dog.objects.filter(data__has_key="owner")
<QuerySet [<Dog: Meg>]>

has_any_keys

傳回資料中包含任何指定鍵的物件。使用 SQL 運算子 ?|。例如

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
>>> Dog.objects.create(name="Meg", data={"owner": "Bob"})
>>> Dog.objects.create(name="Fred", data={})

>>> Dog.objects.filter(data__has_any_keys=["owner", "breed"])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

has_keys

傳回資料中包含所有指定鍵的物件。使用 SQL 運算子 ?&。例如

>>> Dog.objects.create(name="Rufus", data={})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})

>>> Dog.objects.filter(data__has_keys=["breed", "owner"])
<QuerySet [<Dog: Meg>]>

keys

傳回鍵陣列是給定值的物件。請注意,不能保證順序可靠,因此此轉換主要用於結合對 ArrayField 的查詢。使用 SQL 函數 akeys()。例如

>>> Dog.objects.create(name="Rufus", data={"toy": "bone"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})

>>> Dog.objects.filter(data__keys__overlap=["breed", "toy"])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

values

傳回值陣列是給定值的物件。請注意,不能保證順序可靠,因此此轉換主要用於結合對 ArrayField 的查詢。使用 SQL 函數 avals()。例如

>>> Dog.objects.create(name="Rufus", data={"breed": "labrador"})
>>> Dog.objects.create(name="Meg", data={"breed": "collie", "owner": "Bob"})

>>> Dog.objects.filter(data__values__contains=["collie"])
<QuerySet [<Dog: Meg>]>

範圍欄位

有五種範圍欄位類型,對應於 PostgreSQL 中的內建範圍類型。這些欄位用於儲存值的範圍;例如事件的開始和結束時間戳記,或活動適合的年齡範圍。

所有範圍欄位都會轉換為 Python 中的 psycopg 範圍物件,但如果不需要邊界資訊,也接受元組作為輸入。預設值是包含下限,不包含上限,即 [)(有關不同邊界的詳細資訊,請參閱 PostgreSQL 文件)。對於非離散範圍欄位(DateTimeRangeFieldDecimalRangeField),可以使用 default_bounds 引數來變更預設邊界。

IntegerRangeField

class IntegerRangeField(**options)

儲存整數範圍。基於 IntegerField。在資料庫中以 int4range 表示,在 Python 中以 django.db.backends.postgresql.psycopg_any.NumericRange 表示。

無論儲存資料時指定的邊界為何,PostgreSQL 始終以包含下限但不包含上限的規範形式傳回範圍,即 [)

BigIntegerRangeField

class BigIntegerRangeField(**options)

儲存大整數範圍。基於 BigIntegerField。在資料庫中以 int8range 表示,在 Python 中以 django.db.backends.postgresql.psycopg_any.NumericRange 表示。

無論儲存資料時指定的邊界為何,PostgreSQL 始終以包含下限但不包含上限的規範形式傳回範圍,即 [)

DecimalRangeField

class DecimalRangeField(default_bounds='[)', **options)

儲存浮點數值的範圍。基於 DecimalField。在資料庫中以 numrange 表示,在 Python 中以 django.db.backends.postgresql.psycopg_any.NumericRange 表示。

default_bounds

可選。用於列表和元組輸入的 bounds 的值。預設值是包含下限,不包含上限,即 [)(有關不同邊界的詳細資訊,請參閱 PostgreSQL 文件)。 default_bounds 不用於 django.db.backends.postgresql.psycopg_any.NumericRange 輸入。

DateTimeRangeField

class DateTimeRangeField(default_bounds='[)', **options)

儲存時間戳記的範圍。基於 DateTimeField。在資料庫中以 tstzrange 表示,在 Python 中以 django.db.backends.postgresql.psycopg_any.DateTimeTZRange 表示。

default_bounds

選填。用於列表和元組輸入的 bounds 值。預設值是下限包含,上限排除,也就是 [) (請參閱 PostgreSQL 文件以了解關於不同邊界的詳細資訊)。 default_bounds 不適用於 django.db.backends.postgresql.psycopg_any.DateTimeTZRange 輸入。

DateRangeField

class DateRangeField(**options)

儲存日期的範圍。基於 DateField。在資料庫中以 daterange 表示,在 Python 中以 django.db.backends.postgresql.psycopg_any.DateRange 表示。

無論儲存資料時指定的邊界為何,PostgreSQL 始終以包含下限但不包含上限的規範形式傳回範圍,即 [)

查詢範圍欄位

範圍欄位有多個自訂的查詢和轉換。它們適用於以上所有欄位,但我們將使用以下範例模型

from django.contrib.postgres.fields import IntegerRangeField
from django.db import models


class Event(models.Model):
    name = models.CharField(max_length=200)
    ages = IntegerRangeField()
    start = models.DateTimeField()

    def __str__(self):
        return self.name

我們也將使用以下範例物件

>>> import datetime
>>> from django.utils import timezone
>>> now = timezone.now()
>>> Event.objects.create(name="Soft play", ages=(0, 10), start=now)
>>> Event.objects.create(
...     name="Pub trip", ages=(21, None), start=now - datetime.timedelta(days=1)
... )

NumericRange

>>> from django.db.backends.postgresql.psycopg_any import NumericRange

包含函式

如同其他 PostgreSQL 欄位,有三個標準的包含運算子: containscontained_byoverlap,分別使用 SQL 運算子 @><@&&

contains
>>> Event.objects.filter(ages__contains=NumericRange(4, 5))
<QuerySet [<Event: Soft play>]>
contained_by
>>> Event.objects.filter(ages__contained_by=NumericRange(0, 15))
<QuerySet [<Event: Soft play>]>

contained_by 查詢也適用於非範圍欄位類型: SmallAutoFieldAutoFieldBigAutoFieldSmallIntegerFieldIntegerFieldBigIntegerFieldDecimalFieldFloatFieldDateFieldDateTimeField。例如

>>> from django.db.backends.postgresql.psycopg_any import DateTimeTZRange
>>> Event.objects.filter(
...     start__contained_by=DateTimeTZRange(
...         timezone.now() - datetime.timedelta(hours=1),
...         timezone.now() + datetime.timedelta(hours=1),
...     ),
... )
<QuerySet [<Event: Soft play>]>
overlap
>>> Event.objects.filter(ages__overlap=NumericRange(8, 12))
<QuerySet [<Event: Soft play>]>

比較函式

範圍欄位支援標準的查詢:ltgtltegte。這些並不是特別有幫助 - 它們先比較下限,然後僅在必要時比較上限。這也是排序範圍欄位所使用的策略。最好使用特定的範圍比較運算子。

fully_lt

回傳的範圍嚴格小於傳遞的範圍。換句話說,回傳範圍中的所有點都小於傳遞範圍中的所有點。

>>> Event.objects.filter(ages__fully_lt=NumericRange(11, 15))
<QuerySet [<Event: Soft play>]>
fully_gt

回傳的範圍嚴格大於傳遞的範圍。換句話說,回傳範圍中的所有點都大於傳遞範圍中的所有點。

>>> Event.objects.filter(ages__fully_gt=NumericRange(11, 15))
<QuerySet [<Event: Pub trip>]>
not_lt

回傳的範圍不包含任何小於傳遞範圍的點,也就是說,回傳範圍的下限至少是傳遞範圍的下限。

>>> Event.objects.filter(ages__not_lt=NumericRange(0, 15))
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>
not_gt

回傳的範圍不包含任何大於傳遞範圍的點,也就是說,回傳範圍的上限至多是傳遞範圍的上限。

>>> Event.objects.filter(ages__not_gt=NumericRange(3, 10))
<QuerySet [<Event: Soft play>]>
adjacent_to

回傳的範圍與傳遞的範圍共享一個邊界。

>>> Event.objects.filter(ages__adjacent_to=NumericRange(10, 21))
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>

使用邊界查詢

範圍欄位支援多個額外的查詢。

startswith

回傳的物件具有給定的下限。可以鏈接到基本欄位的有效查詢。

>>> Event.objects.filter(ages__startswith=21)
<QuerySet [<Event: Pub trip>]>
endswith

回傳的物件具有給定的上限。可以鏈接到基本欄位的有效查詢。

>>> Event.objects.filter(ages__endswith=10)
<QuerySet [<Event: Soft play>]>
isempty

回傳的物件是空範圍。可以鏈接到 BooleanField 的有效查詢。

>>> Event.objects.filter(ages__isempty=True)
<QuerySet []>
lower_inc

根據傳遞的布林值,回傳具有包含或排除下限的物件。可以鏈接到 BooleanField 的有效查詢。

>>> Event.objects.filter(ages__lower_inc=True)
<QuerySet [<Event: Soft play>, <Event: Pub trip>]>
lower_inf

根據傳遞的布林值,回傳具有無界(無限)或有界下限的物件。可以鏈接到 BooleanField 的有效查詢。

>>> Event.objects.filter(ages__lower_inf=True)
<QuerySet []>
upper_inc

根據傳遞的布林值,回傳具有包含或排除上限的物件。可以鏈接到 BooleanField 的有效查詢。

>>> Event.objects.filter(ages__upper_inc=True)
<QuerySet []>
upper_inf

根據傳遞的布林值,回傳具有無界(無限)或有界上限的物件。可以鏈接到 BooleanField 的有效查詢。

>>> Event.objects.filter(ages__upper_inf=True)
<QuerySet [<Event: Pub trip>]>

定義您自己的範圍類型

PostgreSQL 允許定義自訂範圍類型。Django 的模型和表單欄位實作使用以下基礎類別,而 psycopg 提供 register_range() 來允許使用自訂範圍類型。

class RangeField(**options)

模型範圍欄位的基礎類別。

base_field

要使用的模型欄位類別。

range_type

要使用的範圍類型。

form_field

要使用的表單欄位類別。應該是 django.contrib.postgres.forms.BaseRangeField 的子類別。

class django.contrib.postgres.forms.BaseRangeField

表單範圍欄位的基礎類別。

base_field

要使用的表單欄位。

range_type

要使用的範圍類型。

範圍運算子

class RangeOperators

PostgreSQL 提供了一組 SQL 運算子,可以與範圍資料類型一起使用(請參閱PostgreSQL 文件以了解範圍運算子的完整詳細資訊)。這個類別旨在提供一種方便的方法來避免拼寫錯誤。運算子名稱與對應的查找名稱重疊。

class RangeOperators:
    EQUAL = "="
    NOT_EQUAL = "<>"
    CONTAINS = "@>"
    CONTAINED_BY = "<@"
    OVERLAPS = "&&"
    FULLY_LT = "<<"
    FULLY_GT = ">>"
    NOT_LT = "&>"
    NOT_GT = "&<"
    ADJACENT_TO = "-|-"

RangeBoundary() 表達式

class RangeBoundary(inclusive_lower=True, inclusive_upper=False)
inclusive_lower

如果 True (預設值),則下界是包含的 '[',否則是不包含的 '('

inclusive_upper

如果 False (預設值),則上界是不包含的 ')',否則它是包含的 ']'

RangeBoundary() 表達式代表範圍的邊界。它可以與期望邊界的自訂範圍函數一起使用,例如定義 ExclusionConstraint。請參閱 PostgreSQL 文件以了解完整詳細資訊

返回頂部