PostgreSQL 特定模型欄位¶
這些欄位都可從 django.contrib.postgres.fields
模組取得。
為這些欄位建立索引¶
Index
和 Field.db_index
都會建立 B 樹索引,這在查詢複雜資料類型時並非特別有用。像是 GinIndex
和 GistIndex
等索引更為適合,但索引的選擇取決於您正在使用的查詢。一般而言,GiST 可能適合範圍欄位和HStoreField
,而 GIN 可能對ArrayField
有幫助。
ArrayField
¶
- class ArrayField(base_field, size=None, **options)¶
用於儲存資料列表的欄位。可以使用大多數欄位類型,並且您可以將另一個欄位實例作為
base_field
傳遞。您也可以指定size
。ArrayField
可以巢狀使用以儲存多維陣列。如果您給予欄位
default
,請確保它是可呼叫的,例如list
(對於空預設值) 或返回列表的可呼叫函式。錯誤地使用default=[]
會建立一個可變的預設值,該預設值在ArrayField
的所有實例之間共享。- base_field¶
這是必要參數。
指定陣列的底層資料類型和行為。它應該是
Field
的子類別的實例。例如,它可以是IntegerField
或CharField
。允許使用大多數欄位類型,但不允許使用處理關聯資料的欄位 (ForeignKey
、OneToOneField
和ManyToManyField
) 和檔案欄位 (FileField
和ImageField
)。可以巢狀使用陣列欄位 - 您可以指定
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
)。若要使用此欄位,您需要
在您的
INSTALLED_APPS
中新增'django.contrib.postgres'
。在 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
查詢。
注意
鍵轉換也可以與以下查詢串連: contains
、 icontains
、 endswith
、 iendswith
、 iexact
、 regex
、 iregex
、 startswith
和 istartswith
查詢。
警告
由於任何字串都可能是 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 文件)。對於非離散範圍欄位(DateTimeRangeField
和 DecimalRangeField
),可以使用 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
表示。
DateTimeRangeField
¶
- class DateTimeRangeField(default_bounds='[)', **options)¶
儲存時間戳記的範圍。基於
DateTimeField
。在資料庫中以tstzrange
表示,在 Python 中以django.db.backends.postgresql.psycopg_any.DateTimeTZRange
表示。
DateRangeField
¶
查詢範圍欄位¶
範圍欄位有多個自訂的查詢和轉換。它們適用於以上所有欄位,但我們將使用以下範例模型
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 欄位,有三個標準的包含運算子: contains
、 contained_by
和 overlap
,分別使用 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
查詢也適用於非範圍欄位類型: SmallAutoField
、 AutoField
、 BigAutoField
、 SmallIntegerField
、 IntegerField
、 BigIntegerField
、 DecimalField
、 FloatField
、 DateField
和 DateTimeField
。例如
>>> 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>]>
比較函式¶
範圍欄位支援標準的查詢:lt
、 gt
、 lte
和 gte
。這些並不是特別有幫助 - 它們先比較下限,然後僅在必要時比較上限。這也是排序範圍欄位所使用的策略。最好使用特定的範圍比較運算子。
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 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 文件以了解完整詳細資訊。