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
¶
在 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>]>
索引转换¶
索引转换索引到数组中。可以使用任何非负整数。如果它超过了数组的 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 []>
注意
PostgreSQL 在编写原始 SQL 时使用基于 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>]>
注意
PostgreSQL 在编写原始 SQL 时使用基于 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
¶
在 HStoreField
上覆盖了 contains
查找。返回的对象是给定的 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 Range 对象,但如果不需要边界信息,也可以接受元组作为输入。默认情况下,下界包含,上界排除,即 [)
(有关 不同边界 的详细信息,请参阅 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 文档。