所有这些字段都可以从 django.contrib.postgres.field
模块中获得。
Index
和 Field.db_index
都创建了一个 B 树索引,在查询复杂的数据类型时并不是特别有用。像 GinIndex
和 GistIndex
这样的索引比较适合,不过索引的选择取决于你使用的查询。一般来说,GiST 可能是 range 字段 和 HStoreField
的好选择,而 GIN 可能对 ArrayField
有帮助。
ArrayField
¶一个用于存储数据列表的字段。大多数字段类型都可以使用,你可以通过另一个字段实例作为 base_field
。你也可以指定一个 size
。ArrayField
可以嵌套来存储多维数组。
如果你给字段一个 default
,确保它是一个可调用对象,比如 list
(对于一个空的默认值),或者一个返回一个列表的可调用对象(比如一个函数)。错误地使用 default=[]
会创建一个可变的默认值,这个默认值在 ArrayField
的所有实例之间共享。
这是一个必要的参数。
指定数组的底层数据类型和行为。它应该是 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,
)
数据库和模型之间的值的转换、数据和配置的验证以及序列化都是委托给底层基础字段的。
这是一个可选的参数。
如果传入,数组将有一个指定的最大大小。这将被传递给数据库,尽管 PostgreSQL 目前并没有强制执行这个限制。
Note
嵌套 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],
]
)
如果需要不规则的形状,则应将底层字段设为 null,并将值用 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
¶ArrayField
上重写了 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__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>]>
Index 转换将索引转换为数组。可以使用任何非负整数。如果超过数组的 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 []>
Note
PostgreSQL 在编写原始 SQL 时,对数组字段使用基于 1 的索引。然而这些索引和 slices
中使用的索引使用基于 0 的索引,以与 Python 保持一致。
Slice 转换获取数组的一个切片。可以使用两个非负整数,用一个下划线分隔。转换后可用的查询不会改变。例如:
>>> 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>]>
Note
PostgreSQL 在编写原始 SQL 时,对数组字段使用基于 1 的索引。然而这些分片和 indexes
中使用的分片使用基于 0 的索引,以与 Python 保持一致。
带索引和切片的多维数组
PostgreSQL 在多维数组上使用索引和切片时,有一些相当神秘的行为。使用索引向下到达最终的底层数据总是可行的,但是大多数其他的切片在数据库层面的行为很奇怪,不能被 Django 以逻辑的、一致的方式支持。
HStoreField
¶一个用于存储键值对的字段。使用的 Python 数据类型是 dict
。键必须是字符串,值可以是字符串或空值(Python 中的 None
)。
要使用该字段,你需要:
在你的 INSTALLED_APPS
中增加 'django.contrib.postgres'
。
在 PostgreSQL 中 安装 hstore 扩展。
如果你跳过第一步,你会看到一个错误,比如 can't adapt type 'dict'
,如果你跳过第二步,你会看到 type "hstore" does not exist
。
Note
在某些情况下,可能需要要求或限制对某个字段有效的键。这可以使用 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
查找来代替。
Note
关键词转换也可以与以下链式进行: contains
、icontains
、endswith
、iendswith
、iexact
、regex
、iregex
、startswith
和 istartswith
查询。
Warning
由于任何字符串都可能是 hstore 值中的一个键,因此除了下面列出的以外的任何查询都将被解释为键查询。不会出现任何错误。要格外小心输入错误,并始终检查你的查询是否按照你的意图工作。
contains
¶HStoreField
上重写了 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__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 Range 对象,但如果不需要边界信息,也可以接受元组作为输入。默认是包含下界,不包含上界,即 [)
(有关 不同边界 的详细信息,请参阅 PostgreSQL 文档)。对于非离散范围字段(例如 DateTimeRangeField
和 DecimalRangeField
),可以使用 default_bounds
参数更改默认边界。
PostgreSQL normalizes a range with no points to the empty range
A range with equal values specified for an included lower bound and an
excluded upper bound, such as Range(datetime.date(2005, 6, 21),
datetime.date(2005, 6, 21))
or [4, 4)
, has no points. PostgreSQL will
normalize the value to empty when saving to the database, and the original
bound values will be lost. See the PostgreSQL documentation for details.
IntegerRangeField
¶存储一组整数范围。基于 IntegerField
。在数据库中由 int4range
表示,在 Python 中由 django.db.backends.postgresql.psycopg_any.NumericRange
表示。
无论在保存数据时指定的边界是什么,PostgreSQL 总是以规范的形式返回一个包括下限和排除上限的范围,即 [)
。
BigIntegerRangeField
¶存储一组大整数范围。基于 BigIntegerField
。在数据库中由 int8range
表示,在 Python 中由 django.db.backends.postgresql.psycopg_any.NumericRange
表示。
无论在保存数据时指定的边界是什么,PostgreSQL 总是以规范的形式返回一个包括下限和排除上限的范围,即 [)
。
DecimalRangeField
¶存储一组浮点数值范围。基于 DecimalField
。在数据库中由 numrange
表示,在 Python 中由 django.db.backends.postgresql.psycopg_any.NumericRange
表示。
DateTimeRangeField
¶存储一组时间戳范围。基于 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()
函数来允许使用自定义范围类型。
模型范围字段的基类。
要使用的模型字段类。
要使用的范围类型。
要使用的表单字段类。应该是 django.contrib.postgres.forms.BaseRangeField
的子类。
PostgreSQL 提供了一组 SQL 运算符,这些运算符可以和范围数据类型一起使用(参见 `the PostgreSQL documentation for the full details of range operators `_ )。这个类的目的是作为一种方便的方法,以避免排版错误。运算符名称与相应的查找名称重叠。
class RangeOperators:
EQUAL = "="
NOT_EQUAL = "<>"
CONTAINS = "@>"
CONTAINED_BY = "<@"
OVERLAPS = "&&"
FULLY_LT = "<<"
FULLY_GT = ">>"
NOT_LT = "&>"
NOT_GT = "&<"
ADJACENT_TO = "-|-"
如果 True
(默认),则下界为包含 '['
,否则为不包含 '('
。
如果 False
(默认),则上界为包含 ')'
,否则为不包含 ']'
。
RangeBoundary()
表达式表示范围边界。它可以与自定义的范围函数一起使用,预期边界,例如定义 ExclusionConstraint
。参见 `the PostgreSQL documentation for the full details `_ 。
4月 27, 2025