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 文档。