PostgreSQL 特有模型字段¶
所有这些字段都可以从 django.contrib.postgres.field
模块中获得。
对这些字段进行索引¶
Index
和 Field.db_index
都创建了一个 B 树索引,在查询复杂的数据类型时并不是特别有用。像 GinIndex
和 GistIndex
这样的索引比较适合,不过索引的选择取决于你使用的查询。一般来说,GiST 可能是 range 字段 和 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
)之外,大多数字段类型都是允许的。可以嵌套数组字段——你可以指定一个
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],
])
如果需要不规则的形状,则应将底层字段设为 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
¶
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'])
>>> 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>]>
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 以逻辑的、一致的方式支持。
CIText
字段¶
-
class
CIText
(**options)¶ 一个用于创建由 citext 类型支持的不区分大小写的文本字段的混入。在使用它之前,请阅读 性能考虑因素 。
要使用
citext
,在第一次CreateModel
迁移操作之前,使用CITextExtension
操作 在PostgreSQL 中设置 citext 扩展。如果你使用的是
ArrayField
的CIText
字段,你必须在你的INSTALLED_APPS
中添加'django.contrib.postgres'
,否则字段值会以字符串形式出现,比如'{thoughts,django}'
。提供了几个使用混入的字段:
-
class
CICharField
(**options)¶
-
class
CIEmailField
(**options)¶
-
class
CITextField
(**options)¶ 这些字段分别是
CharField
、EmailField
和TextField
的子类。max_length
不会在数据库中强制执行,因为citext
与 PostgreSQL 的text
类型相似。
不区分大小写的字节序
在 PostgreSQL 12+ 上,最好使用非确定的字节序,而不是 citext
扩展。你可以使用 CreateCollation
迁移操作来创建它们。更多细节,请参阅 使用迁移来管理整理 和 PostgreSQL 关于`non-deterministic collations`_ 的文档。
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>]>
如果你想查询的键与另一个查找的名称冲突,你需要使用 hstorefield.contains
查找来代替。
注解
Key transforms can also be chained with: contains
,
icontains
, endswith
, iendswith
,
iexact
, regex
, iregex
, startswith
,
and istartswith
lookups.
警告
由于任何字符串都可能是 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>]>
JSONField
¶
-
class
JSONField
(encoder=None, **options)¶ 一个用于存储 JSON 编码数据的字段。在 Python 中,数据以其 Python 本地格式表示:字典、列表、字符串、数字、布尔值和
None
。-
encoder
¶ 一个可选的 JSON 编码类,用于序列化标准 JSON 序列化器不支持的数据类型(
datetime
、uuid
等)。例如,你可以使用DjangoJSONEncoder
类或任何其他json.JSONEncoder
子类。当从数据库中获取值时,它将以自定义编码器选择的格式(最常见的是字符串),因此你需要采取额外的步骤将值转换回初始数据类型(
Model.from_db()
和Field.from_db_value()
是两个可能的钩子)。你的反序列化可能需要考虑到你无法确定输入类型的事实。例如,你有可能返回一个datetime
,但实际上是一个字符串,而这个字符串恰好与datetime
的格式相同。
如果你给字段一个
default
,确保它是一个可调用对象,比如dict
(对于一个空的默认值),或者一个返回字典的可调用对象(比如一个函数)。错误地使用default={}
会创建一个可变的默认值,这个默认值在JSONField
的所有实例之间共享。-
注解
PostgreSQL 有两种基于 JSON 的原生数据类型: json
和 jsonb
。json
和 jsonb
。它们之间的主要区别在于它们的存储方式和查询方式。PostgreSQL 的 json
字段是作为 JSON 的原始字符串表示来存储的,当根据键来查询时,必须同时进行解码。jsonb
字段是基于 JSON 的实际结构存储的,它允许索引。这样做的代价是在写入 jsonb
字段时增加了一点成本。JSONField
使用 jsonb
。
3.1 版后已移除: 使用 django.db.models.JSONField
代替。
查询 JSONField
¶
详见 查询 JSONField。
范围字段¶
有五种范围字段类型,对应 PostgreSQL 中内置的范围类型。这些字段用来存储一个范围的值,例如一个事件的开始和结束时间戳,或者一个活动适合的年龄范围。
所有的范围字段都翻译成 Python 中的 psycopg2 范围对象,但如果不需要边界信息,也接受元组作为输入。默认是包含下界,排除上界,也就是 [)
(关于 不同的边界 ),请参见 PostgreSQL 文档。
IntegerRangeField
¶
-
class
IntegerRangeField
(**options)¶ 存储一个整数范围。基于一个
IntegerField
。在数据库中用int4range
表示,在 Python 中用一个NumericRange
表示。无论在保存数据时指定的边界是什么,PostgreSQL 总是以规范的形式返回一个包括下限和排除上限的范围,即
[)
。
BigIntegerRangeField
¶
-
class
BigIntegerRangeField
(**options)¶ 存储大整数的范围。基于一个
BigIntegerField
。在数据库中用int8range
表示,在 Python 中用一个NumericRange
表示。无论在保存数据时指定的边界是什么,PostgreSQL 总是以规范的形式返回一个包括下限和排除上限的范围,即
[)
。
DecimalRangeField
¶
-
class
DecimalRangeField
(**options)¶ 存储浮点值的范围。基于
DecimalField
。在数据库中用numrange
表示,在 Python 中用一个NumericRange
表示。
DateTimeRangeField
¶
-
class
DateTimeRangeField
(**options)¶ 存储一系列的时间戳。基于
DateTimeField
。在数据库中用tstzrange
表示,在 Python 中用一个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 psycopg2.extras 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 psycopg2.extras 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>]>
增加了对 SmallAutoField
、AutoField
、BigAutoField
、SmallIntegerField
和 DecimalField
的支持。
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 的模型和表单字段实现使用下面的基类,psycopg2 提供了一个 register_range()
来允许使用自定义范围类型。
-
class
RangeField
(**options)¶ 模型范围字段的基类。
-
base_field
¶ 要使用的模型字段类。
-
range_type
¶ 要使用的 psycopg2 范围类型。
-
form_field
¶ 要使用的表单字段类。应该是
django.contrib.postgres.forms.BaseRangeField
的子类。
-
范围操作¶
-
class
RangeOperators
¶
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 = '-|-'
RangeBoundary() 表达式¶
-
class
RangeBoundary
(inclusive_lower=True, inclusive_upper=False)¶ -
inclusive_lower
¶ 如果
True
(默认),则下界为包含'['
,否则为不包含'('
。
-
inclusive_upper
¶ 如果
False
(默认),则上界为包含')'
,否则为不包含']'
。
-
RangeBoundary()
表达式表示范围边界。它可以与自定义的范围函数一起使用,预期边界,例如定义 ExclusionConstraint
。参见 `the PostgreSQL documentation for the full details `_ 。