Skip to main content

PostgreSQL特定的模型字段

所有这些字段都可从 django.contrib.postgres.fields 模块获得。

ArrayField

class ArrayField(base_field, size=None, **options)

用于存储数据列表的字段。可以使用大多数字段类型,您只需传递另一个字段实例作为 base_field。您还可以指定 sizeArrayField 可以嵌套存储多维数组。

如果你给这个字段一个 default,确保它是一个可调用,如 list (一个空的默认)或一个可调用,返回一个列表(如一个函数)。不正确地使用 default=[] 会创建一个可变默认值,在 ArrayField 的所有实例之间共享。

base_field

这是必需的参数。

指定数组的底层数据类型和行为。它应该是 Field 的子类的实例。例如,它可以是 IntegerFieldCharField。除了处理关系数据(ForeignKeyOneToOneFieldManyToManyField)的字段之外,大多数字段类型是允许的。

可以嵌套数组字段 - 您可以指定 ArrayField 的实例作为 base_field。例如:

from django.db import models
from django.contrib.postgres.fields import ArrayField

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.db import models
from django.contrib.postgres.fields import ArrayField

class Post(models.Model):
    name = models.CharField(max_length=200)
    tags = ArrayField(models.CharField(max_length=200), blank=True)

    def __str__(self):  # __unicode__ on Python 2
        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 []>

注解

在写原始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的逻辑一致方式支持。

索引 ArrayField

目前使用 db_index 将创建一个 btree 索引。这不提供特别重要的帮助查询。更有用的索引是 GIN 索引,您应该使用 RunSQL 操作创建索引。

HStoreField

class HStoreField(**options)

用于存储字符串到字符串的映射的字段。所使用的Python数据类型是 dict

要使用此字段,您需要:

  1. 在您的 INSTALLED_APPS 中添加 'django.contrib.postgres'

  2. 设置hstore扩展 在PostgreSQL。

如果跳过第一步,您将看到类似于 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):  # __unicode__ on Python 2
        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 查找。

警告

因为任何字符串可以是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

New in Django 1.9.

返回对象,其中给定的键在数据中。使用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函数 avalues()。例如:

>>> 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

New in Django 1.9.
class JSONField(**options)

用于存储JSON编码数据的字段。在Python中,数据以其Python原生格式表示:字典,列表,字符串,数字,布尔值和 None

如果要存储其他数据类型,则需要先将其序列化。例如,您可以将 datetime 转换为字符串。当您从数据库检索数据时,您可能还需要将字符串转换回 datetime。有一些第三方 JSONField 实现自动执行这种事情。

如果你给一个 default 字段,确保它是一个可调用,如 dict (一个空的默认值)或一个可调用,返回一个dict(如一个函数)。不正确地使用 default={} 会创建一个可变默认值,在 JSONField 的所有实例之间共享。

注解

PostgreSQL有两种基于JSON的数据类型:jsonjsonb。它们之间的主要区别是它们是如何存储的以及如何查询它们。 PostgreSQL的 json 字段存储为JSON的原始字符串表示形式,并且必须在基于键查询时即时解码。 jsonb 字段基于允许索引的JSON的实际结构存储。在写入 jsonb 字段时,权衡是一个小的额外成本。 JSONField 使用 jsonb

因此,该字段需要PostgreSQL≥9.4和Psycopg2≥2.5.4

查询 JSONField

我们将使用以下示例模型:

from django.contrib.postgres.fields import JSONField
from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = JSONField()

    def __str__(self):  # __unicode__ on Python 2
        return self.name

键,索引和路径查找

要基于给定的字典键进行查询,只需使用该键作为查找名称:

>>> Dog.objects.create(name='Rufus', data={
...     'breed': 'labrador',
...     'owner': {
...         'name': 'Bob',
...         'other_pets': [{
...             'name': 'Fishy',
...         }],
...     },
... })
>>> Dog.objects.create(name='Meg', data={'breed': 'collie'})

>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>

多个键可以链接在一起以形成路径查找:

>>> Dog.objects.filter(data__owner__name='Bob')
<QuerySet [<QuerySet <Dog: Rufus>]>

如果键是整数,它将被解释为数组中的索引查找:

>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
<QuerySet [<Dog: Rufus>]>

如果要查询的键与另一个查找的名称冲突,请改用 jsonfield.contains 查找。

如果只使用一个键或索引,则使用SQL运算符 ->。如果使用多个操作符,则使用 #> 操作符。

警告

由于任何字符串都可能是JSON对象中的键,因此除下面列出的之外的任何查找都将被解释为键查找。不会产生错误。请特别小心输入错误,并始终检查您的查询工作,因为你打算。

遏制和关键操作

JSONFieldHStoreField 共享与遏制和密钥相关的查找。

范围字段

有五个范围字段类型,对应于PostgreSQL中的内置范围类型。这些字段用于存储值的范围;例如事件的开始和结束时间戳,或活动适合的年龄范围。

所有范围字段都转换为python中的 psycopg2范围对象,但如果不需要边界信息,也接受元组作为输入。默认值为下限,上限为排除;即 [)

IntegerRangeField

class IntegerRangeField(**options)

存储整数范围。基于 IntegerField。由数据库中的 int4range 和Python中的 NumericRange 表示。

无论在保存数据时指定的边界如何,PostgreSQL总是返回一个规范形式的范围,该范围包括下限并排除上限;即 [)

BigIntegerRangeField

class BigIntegerRangeField(**options)

存储大整数的范围。基于 BigIntegerField。由数据库中的 int8range 和Python中的 NumericRange 表示。

无论在保存数据时指定的边界如何,PostgreSQL总是返回一个规范形式的范围,该范围包括下限并排除上限;即 [)

FloatRangeField

class FloatRangeField(**options)

存储浮点值的范围。基于 FloatField。由数据库中的 numrange 和Python中的 NumericRange 表示。

DateTimeRangeField

class DateTimeRangeField(**options)

存储时间戳范围。基于 DateTimeField。由数据库中的 tztsrange 和Python中的 DateTimeTZRange 表示。

DateRangeField

class DateRangeField(**options)

存储日期范围。基于 DateField。由数据库中的 daterange 和Python中的 DateRange 表示。

无论在保存数据时指定的边界如何,PostgreSQL总是返回一个规范形式的范围,该范围包括下限并排除上限;即 [)

查询范围字段

范围字段有许多自定义查找和变换。它们可用于所有上述字段,但我们将使用以下示例模型:

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):  # __unicode__ on Python 2
        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字段一样,有三个标准包含运算符:containscontained_byoverlap,分别使用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>]>
New in Django 1.9.

contained_by 查找也可用于非范围字段类型:IntegerFieldBigIntegerFieldFloatFieldDateFieldDateTimeField。例如:

>>> 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>]>
overlap
>>> Event.objects.filter(ages__overlap=NumericRange(8, 12))
<QuerySet [<Event: Soft play>]>

比较函数

范围字段支持标准查找:ltgtltegte。这些不是特别有帮助 - 它们首先比较下限,然后仅在必要时比较上限。这也是用于按范围字段排序的策略。最好使用特定的范围比较运算符。

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 []>

定义自己的范围类型

PostgreSQL允许定义自定义范围类型。 Django模型和表单字段实现使用下面的基类,psycopg2提供了一个 register_range() 允许使用自定义范围类型。

class RangeField(**options)

模型范围字段的基类。

base_field

要使用的模型字段类。

range_type

要使用的psycopg2范围类型。

form_field

要使用的表单字段类。应该是 django.contrib.postgres.forms.BaseRangeField 的子类。

class django.contrib.postgres.forms.BaseRangeField

表单范围字段的基类。

base_field

要使用的表单字段。

range_type

要使用的psycopg2范围类型。