QuerySet API 参考 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/ref/models/querysets
跳转至:导航、​搜索

QuerySet API 参考

本文档描述了 QuerySet API 的详细信息。 它建立在 模型数据库查询 指南中提供的材料之上,因此您可能希望在阅读本文之前先阅读并理解这些文档。

在本参考文献中,我们将使用 数据库查询指南 中介绍的 示例网络日志模型

当评估 QuerySets 时

在内部,QuerySet 可以被构造、过滤、切片,并且通常在不实际访问数据库的情况下传递。 在您执行某些操作来评估查询集之前,实际上不会发生数据库活动。

您可以通过以下方式评估 QuerySet

  • Iteration. QuerySet 是可迭代的,它会在您第一次对其进行迭代时执行其数据库查询。 例如,这将打印数据库中所有条目的标题:

    for e in Entry.objects.all():
        print(e.headline)

    注意:如果您只想确定是否存在至少一个结果,请不要使用此选项。 使用 exists() 效率更高。

  • 切片。Limiting QuerySets 中所述,可以使用 Python 的数组切片语法对 QuerySet 进行切片。 切片一个未赋值的 QuerySet 通常会返回另一个未赋值的 QuerySet,但如果使用切片语法的“step”参数,Django 将执行数据库查询,并返回一个列表。 对已求值的 QuerySet 进行切片也会返回一个列表。

    另请注意,即使切片未计算的 QuerySet 返回另一个未计算的 QuerySet,也不允许进一步修改(例如,添加更多过滤器或修改排序),因为这不能很好地转换为 SQL它也不会有明确的含义。

  • Pickling/Caching. pickling QuerySets 时所涉及的详细信息,请参阅以下部分。 就本节而言,重要的是从数据库中读取结果。

  • repr(). 当您调用 repr() 时,会评估 QuerySet。 这是为了方便 Python 交互式解释器,因此您可以在交互式使用 API 时立即看到结果。

  • len(). 当您调用 len() 时,会评估 QuerySet。 正如您所料,这将返回结果列表的长度。

    注意:如果您只需要确定集合中的记录数(而不需要实际对象),使用 SQL 的 SELECT COUNT(*) 在数据库级别处理计数会更有效。 正是出于这个原因,Django 提供了一个 count() 方法。

  • list(). 通过调用 list() 强制评估 QuerySet。 例如:

    entry_list = list(Entry.objects.all())
  • bool(). 在布尔上下文中测试 QuerySet,例如使用 bool()orandif 语句,将导致执行查询。 如果至少有一个结果,则QuerySetTrue,否则为False。 例如:

    if Entry.objects.filter(headline="Test"):
       print("There is at least one Entry with the headline Test")

    注意:如果您只想确定是否至少存在一个结果(并且不需要实际对象),则使用 exists() 更有效。

酸洗 QuerySets

如果你 pickle a QuerySet,这将强制所有结果在酸洗之前加载到内存中。 酸洗通常用作缓存的前兆,当重新加载缓存的查询集时,您希望结果已经存在并可供使用(从数据库中读取可能需要一些时间,违背了缓存的目的)。 这意味着当你 unpickle 一个 QuerySet 时,它包含它被pickle 时的结果,而不是当前在数据库中的结果。

如果您只想在稍后从数据库中腌制必要的信息以重新创建 QuerySet,请腌制 QuerySetquery 属性。 然后,您可以使用如下代码重新创建原始 QuerySet(不加载任何结果):

>>> import pickle
>>> query = pickle.loads(s)     # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query            # Restore the original 'query'.

query 属性是一个不透明的对象。 它代表查询构造的内部结构,而不是公共 API 的一部分。 但是,按照此处所述对属性的内容进行pickle 和unpickle 是安全的(并且完全受支持)。

您不能在版本之间共享泡菜

QuerySets 的泡菜仅对用于生成它们的 Django 版本有效。 如果您使用 Django 版本 N 生成 pickle,则无法保证使用 Django N+1 版本可以读取 pickle。 不应将泡菜用作长期存档策略的一部分。

由于pickle 兼容性错误可能难以诊断,例如静默损坏的对象,因此当您尝试在与pickle 版本不同的Django 版本中对查询集进行unpickle 时,会引发RuntimeWarning


QuerySet API

这是 QuerySet 的正式声明:

class QuerySet(model=None, query=None, using=None, hints=None)

通常,当您与 QuerySet 交互时,您会通过 链接过滤器 来使用它。 为了完成这项工作,大多数 QuerySet 方法返回新的查询集。 本节稍后将详细介绍这些方法。

QuerySet 类有两个可用于自省的公共属性:

ordered

True 如果订购了 QuerySet — 即 在模型上有一个 order_by() 子句或默认排序。 False 否则。

db

如果现在执行这个查询,将使用的数据库。

注解

QuerySetquery 参数存在以便专门的查询子类可以重建内部查询状态。 该参数的值是该查询状态的不透明表示,不是公共 API 的一部分。 简单地说:如果你需要问,你不需要使用它。

返回新 QuerySet 的方法

Django 提供了一系列 QuerySet 细化方法,可以修改 QuerySet 返回的结果类型或其 SQL 查询的执行方式。

filter()

filter(**kwargs)

返回包含与给定查找参数匹配的对象的新 QuerySet

查找参数 (**kwargs) 应采用以下 字段查找 中描述的格式。 多个参数通过底层 SQL 语句中的 AND 连接。

如果您需要执行更复杂的查询(例如,使用 OR 语句的查询),您可以使用 Q 对象


exclude()

exclude(**kwargs)

返回一个新的 QuerySet,其中包含 不匹配给定查找参数的对象。

查找参数 (**kwargs) 应采用以下 字段查找 中描述的格式。 多个参数通过底层 SQL 语句中的 AND 连接起来,整个内容包含在一个 NOT() 中。

此示例排除 pub_date 晚于 2005-1-3 且 headline 为“Hello”的所有条目:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

在 SQL 术语中,计算结果为:

SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

此示例排除 pub_date 晚于 2005-1-3 或标题为“您好”的所有条目:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')

在 SQL 术语中,计算结果为:

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'

请注意,第二个例子的限制性更强。

如果您需要执行更复杂的查询(例如,使用 OR 语句的查询),您可以使用 Q 对象


annotate()

annotate(*args, **kwargs)

使用提供的 查询表达式 列表注释 QuerySet 中的每个对象。 表达式可以是一个简单的值、对模型(或任何相关模型)上的字段的引用,或者是对与模型中的对象相关的对象进行计算的聚合表达式(平均值、总和等)。 QuerySet

annotate() 的每个参数都是一个注释,将添加到返回的 QuerySet 中的每个对象。

Django 提供的聚合函数在下面的 Aggregation Functions 中描述。

使用关键字参数指定的注释将使用关键字作为注释的别名。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个别名。 只有引用单个字段的聚合表达式才能是匿名参数。 其他所有内容都必须是关键字参数。

例如,如果您正在操作一个博客列表,您可能想要确定每个博客中已经创建了多少条目:

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42

Blog 模型本身并没有定义 entry__count 属性,但是通过使用关键字参数指定聚合函数,您可以控制注解的名称:

>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42

有关聚合的深入讨论,请参阅 有关聚合的主题指南


order_by()

order_by(*fields)

默认情况下,QuerySet 返回的结果按模型的 Meta 中的 ordering 选项给出的排序元组排序。 您可以使用 order_by 方法在每个 QuerySet 的基础上覆盖它。

例子:

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

上面的结果将按 pub_date 降序排列,然后按 headline 升序排列。 "-pub_date"前面的负号表示降序。 升序是隐含的。 要随机排序,请使用 "?",如下所示:

Entry.objects.order_by('?')

注意:order_by('?') 查询可能既昂贵又缓慢,具体取决于您使用的数据库后端。

要按不同模型中的字段排序,请使用与跨模型关系查询时相同的语法。 即,字段名称,后跟双下划线 (__),后跟新模型中的字段名称,依此类推,适用于您想要加入的模型。 例如:

Entry.objects.order_by('blog__name', 'headline')

如果您尝试按与另一个模型相关的字段排序,Django 将使用相关模型的默认排序,或者如果未指定 Meta.ordering,则按相关模型的主键排序。 例如,由于 Blog 模型没有指定默认排序:

Entry.objects.order_by('blog')

……等同于:

Entry.objects.order_by('blog__id')

如果 Blogordering = ['name'],那么第一个查询集将等同于:

Entry.objects.order_by('blog__name')

您还可以通过在表达式上调用 asc()desc() 来按 查询表达式 排序:

Entry.objects.order_by(Coalesce('summary', 'headline').desc())

asc()desc() 具有控制空值排序方式的参数(nulls_firstnulls_last)。

如果您还使用 distinct(),则在按相关模型中的字段排序时要小心。 有关相关模型排序如何改变预期结果的说明,请参阅 distinct() 中的注释。

注解

允许指定多值字段以对结果进行排序(例如,ManyToManyField 字段,或 ForeignKey 字段的反向关系)。

考虑这个案例:

class Event(Model):
   parent = models.ForeignKey(
       'self',
       on_delete=models.CASCADE,
       related_name='children',
   )
   date = models.DateField()

Event.objects.order_by('children__date')

在这里,每个 Event 可能有多个排序数据; 每个带有多个 childrenEvent 将多次返回到 order_by() 创建的新 QuerySet 中。 换句话说,在 QuerySet 上使用 order_by() 可能会返回比您开始时更多的项目 - 这可能既不符合预期也没有用。

因此,在使用多值字段对结果进行排序时要小心。 如果您可以确定您订购的每件商品只有一个订购数据,那么这种方法应该不会出现问题。 如果没有,请确保结果符合您的预期。


无法指定排序是否应区分大小写。 关于区分大小写,Django 会对结果进行排序,但您的数据库后端通常会对其进行排序。

您可以使用 Lower 将字段转换为小写,这将实现大小写一致的排序:

Entry.objects.order_by(Lower('headline').desc())

如果您不希望对查询应用任何排序,甚至不希望应用默认排序,请调用不带参数的 order_by()

您可以通过检查 QuerySet.ordered 属性来判断查询是否已排序,如果 QuerySet 已以任何方式排序,则该属性将为 True

每个 order_by() 调用将清除任何先前的排序。 例如,此查询将按 pub_date 而不是 headline 排序:

Entry.objects.order_by('headline').order_by('pub_date')

警告

订购不是免费的操作。 您添加到排序中的每个字段都会对您的数据库产生成本。 您添加的每个外键也将隐式包含其所有默认排序。

如果查询未指定排序,则以未指定的顺序从数据库返回结果。 只有在按唯一标识结果中每个对象的一组字段进行排序时,才能保证特定的排序。 例如,如果 name 字段不是唯一的,按它排序并不能保证具有相同名称的对象总是以相同的顺序出现。


reverse()

reverse()

使用 reverse() 方法反转查询集元素的返回顺序。 再次调用 reverse() 会将排序恢复到正常方向。

要检索查询集中的“最后”五个项目,您可以这样做:

my_queryset.reverse()[:5]

请注意,这与在 Python 中从序列末尾进行切片并不完全相同。 上面的例子将首先返回最后一项,然后是倒数第二项,依此类推。 如果我们有一个 Python 序列并查看 seq[-5:],我们将首先看到倒数第五项。 Django 不支持这种访问模式(从末尾切片),因为在 SQL 中不可能有效地做到这一点。

另外,请注意 reverse() 通常只应在具有定义排序的 QuerySet 上调用(例如,当查询定义默认排序的模型时,或使用 order_by( ))。 如果没有为给定的 QuerySet 定义这样的排序,则对其调用 reverse() 没有实际影响(排序在调用 reverse() 之前未定义,之后将保持未定义) .


distinct()

distinct(*fields)

返回在其 SQL 查询中使用 SELECT DISTINCT 的新 QuerySet。 这消除了查询结果中的重复行。

默认情况下,QuerySet 不会消除重复行。 在实践中,这很少成为问题,因为像 Blog.objects.all() 这样的简单查询不会引入重复结果行的可能性。 但是,如果您的查询跨越多个表,则在评估 QuerySet 时可能会得到重复的结果。 那时你会使用 distinct()

注解

order_by() 调用中使用的任何字段都包含在 SQL SELECT 列中。 当与 distinct() 结合使用时,有时会导致意外结果。 如果您按相关模型中的字段排序,这些字段将被添加到选定的列中,否则它们可能会使重复的行看起来不同。 由于额外的列不会出现在返回的结果中(它们仅用于支持排序),因此有时看起来返回的是非不同的结果。

同样,如果您使用 values() 查询来限制选择的列,则任何 order_by()(或默认模型排序)中使用的列仍将涉及并可能影响唯一性的结果。

这里的寓意是,如果您使用 distinct(),请注意按相关型号排序。 同样,当一起使用 distinct()values() 时,按不在 values() 调用中的字段排序时要小心。


仅在 PostgreSQL 上,您可以传递位置参数 (*fields) 以指定 DISTINCT 应适用的字段名称。 这将转换为 SELECT DISTINCT ON SQL 查询。 这就是不同之处。 对于正常的 distinct() 调用,数据库在确定哪些行不同时比较每行中的 each 字段。 对于具有指定字段名称的 distinct() 调用,数据库将仅比较指定的字段名称。

注解

指定字段名称时,必须QuerySet中提供order_by()order_by()中的字段必须以distinct(),顺序相同。

例如,SELECT DISTINCT ON (a) 为您提供 a 列中每个值的第一行。 如果您不指定顺序,您将得到一些任意行。


示例(第一个之后的仅适用于 PostgreSQL):

>>> Author.objects.distinct()
[...]

>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]

>>> Entry.objects.order_by('blog').distinct('blog')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]

注解

请记住, order_by() 使用已定义的任何默认相关模型排序。 您可能必须按关系 _id 或引用字段显式排序,以确保 DISTINCT ON 表达式与 ORDER BY 子句开头的表达式匹配。 例如,如果 Blog 模型通过 name 定义了一个 ordering

Entry.objects.order_by('blog').distinct('blog')

……不起作用,因为查询将按 blog__name 排序,从而与 DISTINCT ON 表达式不匹配。 您必须按关系 _id 字段(在本例中为 blog_id)或引用的字段(blog__pk)明确排序,以确保两个表达式匹配。


values()

values(*fields, **expressions)

返回一个 QuerySet 返回字典,而不是模型实例。

其中每一个字典都代表一个对象,键与模型对象的属性名相对应。

本示例将 values() 的字典与普通模型对象进行比较:

# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

values() 方法采用可选的位置参数 *fields,它指定 SELECT 应限制为的字段名称。 如果您指定字段,则每个字典将仅包含您指定的字段的字段键/值。 如果不指定字段,则每个字典将包含数据库表中每个字段的键和值。

例子:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

values() 方法还接受可选的关键字参数,**expressions,这些参数被传递给 annotate()

>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>

您可以在排序中使用内置和 自定义查找 。 例如:

>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>

2.1 版更改: 添加了对查找的支持。


values() 子句中的聚合应用在同一 values() 子句中的其他参数之前。 如果您需要按另一个值分组,请将其添加到较早的 values() 子句中。 例如:

>>> from django.db.models import Count
>>> Blog.objects.values('entry__authors', entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values('entry__authors').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>

有几个微妙的地方值得一提:

  • 如果您有一个名为 foo 的字段,它是一个 ForeignKey,默认的 values() 调用将返回一个名为 foo_id 的字典键,因为这是名称存储实际值的隐藏模型属性(foo 属性指的是相关模型)。 当你调用 values() 并传入字段名称时,你可以传入 foofoo_id 并且你会得到同样的东西(字典键将匹配字段您传入的名称)。

    例如:

    >>> Entry.objects.values()
    <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    >>> Entry.objects.values('blog')
    <QuerySet [{'blog': 1}, ...]>
    
    >>> Entry.objects.values('blog_id')
    <QuerySet [{'blog_id': 1}, ...]>
  • values()distinct() 一起使用时,请注意排序会影响结果。 有关详细信息,请参阅 distinct() 中的注释。

  • 如果在 extra() 调用后使用 values() 子句,则 extra() 中由 select 参数定义的任何字段必须明确包含在 values() 调用中。 在 values() 调用之后进行的任何 extra() 调用都将忽略其额外的选定字段。

  • values() 之后调用 only()defer() 没有意义,因此这样做会引发 NotImplementedError

  • 组合转换和聚合需要使用两个 annotate() 调用,明确地或作为 values() 的关键字参数。 如上,如果转换已在相关字段类型上注册,则第一个 annotate() 可以省略,因此以下示例是等效的:

    >>> from django.db.models import CharField, Count
    >>> from django.db.models.functions import Lower
    >>> CharField.register_lookup(Lower)
    >>> Blog.objects.values('entry__authors__name__lower').annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.values(
    ...     entry__authors__name__lower=Lower('entry__authors__name')
    ... ).annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.annotate(
    ...     entry__authors__name__lower=Lower('entry__authors__name')
    ... ).values('entry__authors__name__lower').annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>

当您知道只需要少量可用字段中的值并且不需要模型实例对象的功能时,它很有用。 仅选择需要使用的字段会更有效率。

最后,注意可以调用filter()order_by()等。 在 values() 调用之后,这意味着这两个调用是相同的:

Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()

制作 Django 的人更喜欢将所有影响 SQL 的方法放在第一位,然后(可选)任何影响输出的方法(例如 values()),但这并不重要。 这是你真正炫耀个人主义的机会。

您还可以通过 OneToOneFieldForeignKeyManyToManyField 属性引用具有反向关系的相关模型上的字段:

>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>

警告

因为 ManyToManyField 属性和反向关系可以有多个相关行,包括这些可以对结果集的大小产生乘数效应。 如果您在 values() 查询中包含多个这样的字段,这将特别明显,在这种情况下,将返回所有可能的组合。


values_list()

values_list(*fields, flat=False, named=False)

这类似于 values() 不同之处在于它在迭代时返回元组而不是返回字典。 每个元组包含来自传递到 values_list() 调用的相应字段或表达式的值——所以第一项是第一个字段,依此类推。 例如:

>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>

如果只传入单个字段,还可以传入 flat 参数。 如果 True,这将意味着返回的结果是单个值,而不是一元组。 一个例子应该能让区别更清楚:

>>> Entry.objects.values_list('id').order_by('id')
<QuerySet[(1,), (2,), (3,), ...]>

>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>

有多个字段时传入flat是错误的。

您可以通过 named=True 来获得作为 namedtuple() 的结果:

>>> Entry.objects.values_list('id', 'headline', named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>

使用命名元组可能会使使用结果更易读,但代价是将结果转化为命名元组时要付出很小的性能代价。

如果您没有将任何值传递给 values_list(),它将按照声明的顺序返回模型中的所有字段。

一个常见的需求是获取某个模型实例的特定字段值。 为此,请使用 values_list() 后跟 get() 调用:

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'

values()values_list() 都是针对特定用例的优化:在没有创建模型实例的开销的情况下检索数据子集。 当处理多对多和其他多值关系(例如反向外键的一对多关系)时,这个比喻就失效了,因为“一行一个对象”假设不成立。

例如,请注意跨 ManyToManyField 查询时的行为:

>>> Author.objects.values_list('name', 'entry__headline')
<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
 ('George Orwell', 'Why Socialists Do Not Believe in Fun'),
 ('George Orwell', 'In Defence of English Cooking'),
 ('Don Quixote', None)]>

有多个条目的作者多次出现,没有任何条目的作者的条目标题为 None

同样,当查询反向外键时,对于没有任何作者的条目,会出现 None

>>> Entry.objects.values_list('authors')
<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>

dates()

dates(field, kind, order='ASC')

返回一个 QuerySet,其计算结果为 datetime.date 对象列表,表示 QuerySet 内容中特定类型的所有可用日期。

field 应该是您模型的 DateField 的名称。 kind 应为 "year""month""week""day"。 结果列表中的每个 datetime.date 对象都被“截断”为给定的 type

  • "year" 返回字段的所有不同年份值的列表。
  • "month" 返回字段的所有不同年/月值的列表。
  • "week" 返回该字段所有不同年/周值的列表。 所有日期都将是星期一。
  • "day" 返回字段的所有不同年/月/日值的列表。

order,默认为 'ASC',应该是 'ASC''DESC'。 这指定了如何对结果进行排序。

例子:

>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]

2.1 版变更: 增加了“周”支持。


datetimes()

datetimes(field_name, kind, order='ASC', tzinfo=None)

返回一个 QuerySet,其计算结果为 datetime.datetime 对象列表,表示 QuerySet 内容中特定类型的所有可用日期。

field_name 应该是您模型的 DateTimeField 的名称。

kind 应为 "year""month""week""day""hour""minute" , 或 "second"。 结果列表中的每个 datetime.datetime 对象都被“截断”为给定的 type

order,默认为 'ASC',应该是 'ASC''DESC'。 这指定了如何对结果进行排序。

tzinfo 定义日期时间在截断之前转换到的时区。 实际上,根据使用的时区,给定的日期时间具有不同的表示形式。 此参数必须是 datetime.tzinfo 对象。 如果是 None,Django 使用 当前时区:setting:`USE_TZ`False 时无效。

2.1 版变更: 增加了“周”支持。


注解

此函数直接在数据库中执行时区转换。 因此,您的数据库必须能够解释 tzinfo.tzname(None) 的值。 这转化为以下要求:

  • SQLite:没有要求。 使用 pytz(安装 Django 时安装)在 Python 中执行转换。
  • PostgreSQL:无要求(参见 时区 )。
  • Oracle:无要求(请参阅 选择时区文件 )。
  • MySQL:使用 mysql_tzinfo_to_sql 加载时区表。


none()

none()

调用 none() 将创建一个永远不会返回任何对象的查询集,并且在访问结果时不会执行任何查询。 qs.none() 查询集是 EmptyQuerySet 的一个实例。

例子:

>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True

all()

all()

返回当前 QuerySet(或 QuerySet 子类)的 副本 。 这在您可能希望传入模型管理器或 QuerySet 并对结果进行进一步过滤的情况下非常有用。 在任一对象上调用 all() 之后,您肯定会有一个 QuerySet 可以使用。

QuerySet评估 时,它通常会缓存其结果。 如果自评估 QuerySet 后数据库中的数据可能已更改,您可以通过对先前评估的 QuerySet 调用 all() 来获取同一查询的更新结果。


union()

union(*other_qs, all=False)

使用 SQL 的 UNION 运算符组合两个或多个 QuerySet 的结果。 例如:

>>> qs1.union(qs2, qs3)

默认情况下,UNION 运算符仅选择不同的值。 要允许重复值,请使用 all=True 参数。

union()intersection()difference() 返回第一个 QuerySet 类型的模型实例,即使参数是其他的 QuerySet楷模。 只要 SELECT 列表在所有 QuerySet 中都相同(至少是类型,名称无关紧要,只要类型的顺序相同)就可以传递不同的模型。 在这种情况下,您必须使用应用到结果 QuerySetQuerySet 方法中第一个 QuerySet 的列名。 例如:

>>> qs1 = Author.objects.values_list('name')
>>> qs2 = Entry.objects.values_list('headline')
>>> qs1.union(qs2).order_by('name')

此外,只有 LIMITOFFSETCOUNT(*)ORDER BY,并指定列(即 切片,count()order_by()values()/values_list()) 允许在结果 [X123X ]。 此外,数据库对组合查询中允许的操作进行了限制。 例如,大多数数据库不允许在组合查询中使用 LIMITOFFSET


intersection()

intersection(*other_qs)

使用 SQL 的 INTERSECT 运算符返回两个或多个 QuerySet 的共享元素。 例如:

>>> qs1.intersection(qs2, qs3)

有关一些限制,请参阅 union()


difference()

difference(*other_qs)

使用 SQL 的 EXCEPT 运算符仅保留 QuerySet 中存在的元素,而不保留其他一些 QuerySet 中的元素。 例如:

>>> qs1.difference(qs2, qs3)

有关一些限制,请参阅 union()


extra()

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

有时,Django 查询语法本身无法轻松表达复杂的 WHERE 子句。 对于这些边缘情况,Django 提供了 extra() QuerySet 修饰符——一个钩子,用于将特定子句注入由 QuerySet 生成的 SQL。

在万不得已的情况下使用这种方法

这是一个旧的 API,我们的目标是在未来的某个时候弃用。 仅当您无法使用其他查询集方法表达查询时才使用它。 如果您确实需要使用它,请使用 QuerySet.extra 关键字 与您的用例一起 提交票证 (请先检查现有票证列表),以便我们可以增强QuerySet API 允许删除 extra()。 我们不再改进或修复此方法的错误。

例如,extra() 的这种用法:

>>> qs.extra(
...     select={'val': "select col from sometable where othercol = %s"},
...     select_params=(someparam,),
... )

相当于:

>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

使用 RawSQL 的主要好处是您可以根据需要设置 output_field。 主要的缺点是,如果您在原始 SQL 中引用查询集的某个表别名,那么 Django 可能会更改该别名(例如,当查询集用作另一个查询中的子查询时)。


警告

使用 extra() 时应该非常小心。 每次使用它时,都应该使用 params 对用户可以控制的任何参数进行转义,以防止 SQL 注入攻击。

您也不得在 SQL 字符串中引用占位符。 由于 %s 周围的引号,此示例容易受到 SQL 注入攻击:

"select col from sometable where othercol = '%s'"  # unsafe!

您可以阅读有关 Django SQL 注入保护 工作原理的更多信息。


根据定义,这些额外的查找可能无法移植到不同的数据库引擎(因为您明确地编写 SQL 代码)并且违反了 DRY 原则,因此您应该尽可能避免它们。

指定 paramsselectwheretables 中的一个或多个。 不需要任何参数,但您应该至少使用其中之一。

  • select

    select 参数允许您在 SELECT 子句中放置额外的字段。 它应该是一个将属性名称映射到 SQL 子句以用于计算该属性的字典。

    例子:

    Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})

    因此,每个 Entry 对象都会有一个额外的属性 is_recent,这是一个布尔值,表示条目的 pub_date 是否大于 Jan。 1, 2006.

    Django 将给定的 SQL 片段直接插入到 SELECT 语句中,因此上述示例的结果 SQL 将类似于:

    SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent
    FROM blog_entry;

    下一个例子更高级; 它执行一个子查询,为每个结果 Blog 对象提供一个 entry_count 属性,关联 Entry 对象的整数计数:

    Blog.objects.extra(
        select={
            'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
        },
    )

    在这种特殊情况下,我们利用查询已经在其 FROM 子句中包含 blog_blog 表的事实。

    上述示例的结果 SQL 将是:

    SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
    FROM blog_blog;

    请注意,在 Django 的 select 子句中,大多数数据库引擎所需的围绕子查询的括号不是必需的。 另请注意,某些数据库后端(例如某些 MySQL 版本)不支持子查询。

    在极少数情况下,您可能希望将参数传递给 extra(select=...) 中的 SQL 片段。 为此,请使用 select_params 参数。 由于 select_params 是一个序列,而 select 属性是一个字典,因此需要小心谨慎,以便参数与额外的选择片段正确匹配。 在这种情况下,您应该对 select 值使用 collections.OrderedDict,而不仅仅是普通的 Python 字典。

    这将起作用,例如:

    Blog.objects.extra(
        select=OrderedDict([('a', '%s'), ('b', '%s')]),
        select_params=('one', 'two'))

    如果需要在选择字符串中使用文字 %s,请使用序列 %%s

  • where / tables

    您可以使用 where 定义显式 SQL WHERE 子句——也许是为了执行非显式连接。 您可以使用 tables 手动向 SQL FROM 子句添加表。

    wheretables 都采用字符串列表。 所有 where 参数都与任何其他搜索条件进行“与”运算。

    例子:

    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])

    ...翻译(大致)为以下 SQL:

    SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')

    如果您指定的表已在查询中使用,则在使用 tables 参数时要小心。 当您通过 tables 参数添加额外的表时,Django 假定您希望该表包含额外的时间,如果它已经包含在内。 这就产生了一个问题,因为表名将被赋予一个别名。 如果一个表在 SQL 语句中多次出现,则第二次和后续出现必须使用别名,以便数据库可以区分它们。 如果您指的是在额外 where 参数中添加的额外表,这将导致错误。

    通常,您只会添加尚未出现在查询中的额外表。 但是,如果确实发生了上述情况,则有几种解决方案。 首先,看看您是否可以不包含额外的表并使用查询中已有的表。 如果这是不可能的,请将您的 extra() 调用放在查询集构造的前面,以便您的表是该表的第一次使用。 最后,如果所有其他方法都失败了,请查看生成的查询并重写您的 where 添加以使用为额外表提供的别名。 每次以相同的方式构造查询集时,别名都将相同,因此您可以依赖别名不会更改。

  • order_by

    如果您需要使用通过 extra() 包含的一些新字段或表对结果查询集进行排序,请使用 order_by 参数到 extra() 并传入一系列字符串。 这些字符串应该是模型字段(如查询集上的正常 order_by() 方法),形式为 table_name.column_name 或您在 [ 中指定的列的别名X181X] 参数设置为 extra()

    例如:

    q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
    q = q.extra(order_by = ['-is_recent'])

    这会将 is_recent 为真的所有项目排序到结果集的前面(True 按降序排列在 False 之前)。

    顺便说一下,这表明您可以多次调用 extra() 并且它会按照您的预期运行(每次添加新约束)。

  • params

    上述 where 参数可以使用标准 Python 数据库字符串占位符 — '%s' 来指示数据库引擎应自动引用的参数。 params 参数是要替换的任何额外参数的列表。

    例子:

    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])

    始终使用 params 而不是直接将值嵌入到 where 中,因为 params 将确保根据您的特定后端正确引用值。 例如,引号将被正确转义。

    坏的:

    Entry.objects.extra(where=["headline='Lennon'"])

    好的:

    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])

警告

如果您在 MySQL 上执行查询,请注意 MySQL 的静默类型强制在混合类型时可能会导致意外结果。 如果查询字符串类型的列,但使用的是整数值,MySQL 会在执行比较之前将表中所有值的类型强制为整数。 例如,如果您的表包含值 'abc''def' 并且您查询 WHERE mycolumn=0,则两行都将匹配。 为防止出现这种情况,请在查询中使用该值之前执行正确的类型转换。


defer()

defer(*fields)

在某些复杂的数据建模情况下,您的模型可能包含大量字段,其中一些字段可能包含大量数据(例如,文本字段),或者需要进行昂贵的处理才能将它们转换为 Python 对象。 如果您在最初获取数据时不知道是否需要这些特定字段的情况下使用查询集的结果,您可以告诉 Django 不要从数据库中检索它们。

这是通过将字段的名称传递给 defer() 来完成的:

Entry.objects.defer("headline", "body")

具有延迟字段的查询集仍将返回模型实例。 如果您访问该字段(一次一个,而不是一次访问所有延迟字段),将从数据库中检索每个延迟字段。

您可以多次呼叫 defer()。 每次调用都会向延迟集合添加新字段:

# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")

将字段添加到延迟集中的顺序无关紧要。 使用已被延迟的字段名称调用 defer() 是无害的(该字段仍将被延迟)。

您可以通过使用标准双下划线符号分隔相关字段来延迟加载相关模型中的字段(如果相关模型通过 select_related() 加载):

Blog.objects.select_related().defer("entry__headline", "entry__body")

如果要清除延迟字段集,请将 None 作为参数传递给 defer()

# Load all fields immediately.
my_queryset.defer(None)

模型中的某些字段不会被延迟,即使您要求它们。 你永远不能推迟主键的加载。 如果您使用 select_related() 检索相关模型,则不应推迟从主模型连接到相关模型的字段的加载,否则会导致错误。

注解

defer() 方法(及其表亲 only(),如下)仅适用于高级用例。 当您仔细分析查询并准确了解 ' 您需要什么信息并测量返回您需要的字段与模型的完整字段集之间的差异时,它们提供了一种优化。

即使您认为您处于高级用例情况, 仅在您无法在查询集加载时确定是否需要额外字段时才使用 defer() 。 如果您经常加载和使用数据的特定子集,您可以做出的最佳选择是规范化模型并将未加载的数据放入单独的模型(和数据库表)中。 如果列 must 出于某种原因保留在一个表中,请使用 Meta.managed = False 创建一个模型(请参阅 managed 属性 文档)只包含您通常需要的字段加载并使用您可能会调用 defer() 的地方。 这使您的代码对读者更加明确,速度稍快,并且在 Python 进程中消耗的内存更少。

例如,这两个模型都使用相同的底层数据库表:

class CommonlyUsedModel(models.Model):
    f1 = models.CharField(max_length=10)

    class Meta:
        managed = False
        db_table = 'app_largetable'

class ManagedModel(models.Model):
    f1 = models.CharField(max_length=10)
    f2 = models.CharField(max_length=10)

    class Meta:
        db_table = 'app_largetable'

# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.all().defer('f2')

如果很多字段需要在非托管模型中重复,最好的办法是创建一个共享字段的抽象模型,然后让非托管模型和托管模型从抽象模型中继承。


注解

当为具有延迟字段的实例调用 save() 时,只会保存加载的字段。 有关更多详细信息,请参阅 save()


only()

only(*fields)

only() 方法或多或少与 defer() 相反。 您可以使用在检索模型时应延迟 而非 的字段调用它。 如果您的模型几乎所有字段都需要延迟,则使用 only() 指定补充字段集可以简化代码。

假设您有一个包含字段 nameagebiography 的模型。 以下两个查询集在延迟字段方面是相同的:

Person.objects.defer("age", "biography")
Person.objects.only("name")

每当您调用 only() 时,它会 替换 立即加载的字段集。 该方法的名称是助记符:only 那些字段会立即加载; 其余的被推迟。 因此,对 only() 的连续调用导致只考虑最终字段:

# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")

由于 defer() 以增量方式起作用(将字段添加到延迟列表中),您可以组合对 only()defer() 的调用,并且事情将按逻辑进行:

# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")

# Final result loads headline and body immediately (only() replaces any
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")

defer() 文档注释中的所有注意事项也适用于 only()。 谨慎使用它,并且只有在用尽你的其他选择之后。

使用 only() 并省略使用 select_related() 请求的字段也是一个错误。

注解

当为具有延迟字段的实例调用 save() 时,只会保存加载的字段。 有关更多详细信息,请参阅 save()


using()

using(alias)

如果您使用多个数据库,此方法用于控制 QuerySet 将针对哪个数据库进行评估。 此方法采用的唯一参数是数据库的别名,如 :setting:`DATABASES` 中所定义。

例如:

# queries the database with the 'default' alias.
>>> Entry.objects.all()

# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')

select_for_update()

select_for_update(nowait=False, skip_locked=False, of=())

返回将锁定行直到事务结束的查询集,在支持的数据库上生成 SELECT ... FOR UPDATE SQL 语句。

例如:

from django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
    for entry in entries:
        ...

当查询集被评估时(在本例中为 for entry in entries),所有匹配的条目将被锁定,直到事务块结束,这意味着将阻止其他事务更改或获取对它们的锁。

通常,如果另一个事务已经获得了对选定行之一的锁定,则查询将阻塞,直到锁定被释放。 如果这不是您想要的行为,请调用 select_for_update(nowait=True)。 这将使调用非阻塞。 如果另一个事务已经获取了冲突锁,则在评估查询集时将引发 DatabaseError。 您也可以使用 select_for_update(skip_locked=True) 来忽略锁定的行。 nowaitskip_locked 是互斥的,在启用这两个选项的情况下尝试调用 select_for_update() 将导致 ValueError

默认情况下,select_for_update() 锁定查询选择的所有行。 例如,除了查询集模型的行之外,select_related() 中指定的相关对象的行也被锁定。 如果不需要,请使用与 select_related() 相同的字段语法在 select_for_update(of=(...)) 中指定要锁定的相关对象。 使用值 'self' 来引用查询集的模型。

select_for_update(of=(...)) 中锁定父母模型

如果要在使用 多表继承 时锁定父模型,则必须在 of 参数中指定父链接字段(默认为 <parent_model_name>_ptr)。 例如:

Restaurant.objects.select_for_update(of=('self', 'place_ptr'))

您不能在可空关系上使用 select_for_update()

>>> Person.objects.select_related('hometown').select_for_update()
Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join

为了避免这种限制,如果您不关心它们,您可以排除空对象:

>>> Person.objects.select_related('hometown').select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>

目前,postgresqloraclemysql 数据库后端支持 select_for_update()。 但是,MySQL 不支持 [X35X] 参数,并且仅在 MySQL 8.0.1+ 上支持 nowaitskip_locked 参数。

使用不支持这些选项的数据库后端(例如 MySQL)将 nowait=Trueskip_locked=Trueof 传递给 select_for_update(),会引发 NotSupportedError[ X160X]。 这可以防止代码意外阻塞。

在支持 SELECT ... FOR UPDATE 的后端在自动提交模式下使用 select_for_update() 评估查询集是一个 TransactionManagementError 错误,因为在这种情况下行未锁定。 如果允许,这将促进数据损坏,并且很容易由调用预期在事务之外的事务中运行的代码引起。

在不支持 SELECT ... FOR UPDATE 的后端(例如 SQLite)上使用 select_for_update() 将不起作用。 SELECT ... FOR UPDATE 不会添加到查询中,如果在自动提交模式下使用 select_for_update(),则不会引发错误。

警告

尽管 select_for_update() 在自动提交模式下通常会失败,但由于 TestCase 自动将每个测试包装在一个事务中,即使在 之外调用 TestCase 中的 select_for_update() atomic() 块将(可能出乎意料地)通过而不引发 TransactionManagementError。 要正确测试 select_for_update(),您应该使用 TransactionTestCase


可能不支持某些表达方式

PostgreSQL 不支持 select_for_update()Window 表达式。


raw()

raw(raw_query, params=None, translations=None)

获取原始 SQL 查询,执行它,并返回一个 django.db.models.query.RawQuerySet 实例。 这个 RawQuerySet 实例可以像普通的 QuerySet 一样迭代以提供对象实例。

有关详细信息,请参阅 执行原始 SQL 查询

警告

raw() 总是触发一个新的查询并且不考虑之前的过滤。 因此,通常应该从 Manager 或新的 QuerySet 实例调用它。


返回新 QuerySet 的运算符

组合的查询集必须使用相同的模型。

与 (&)

使用 SQL AND 运算符组合两个 QuerySet

以下是等效的:

Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1, y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) & Q(y=2))

SQL 等价于:

SELECT ... WHERE x=1 AND y=2

或 (|)

使用 SQL OR 运算符组合两个 QuerySet

以下是等效的:

Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

SQL 等价于:

SELECT ... WHERE x=1 OR y=2

不返回 QuerySets 的方法

以下 QuerySet 方法评估 QuerySet 并返回 以外的QuerySet

这些方法不使用缓存(参见 Caching 和 QuerySets)。 相反,他们每次被调用时都会查询数据库。

get()

get(**kwargs)

返回与给定查找参数匹配的对象,该对象应采用 字段查找 中描述的格式。

get() 如果找到多个对象,则引发 MultipleObjectsReturnedMultipleObjectsReturned 异常是模型类的一个属性。

get() 如果未找到给定参数的对象,则会引发 DoesNotExist 异常。 这个异常是模型类的一个属性。 例子:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist

DoesNotExist 异常继承自 django.core.exceptions.ObjectDoesNotExist,因此您可以针对多个 DoesNotExist 异常。 例子:

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print("Either the entry or blog doesn't exist.")

如果您希望查询集返回一行,则可以使用不带任何参数的 get() 来返回该行的对象:

entry = Entry.objects.filter(...).exclude(...).get()

create()

create(**kwargs)

一种创建对象并将其全部保存在一个步骤中的便捷方法。 因此:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

和:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

是等效的。

force_insert 参数在别处有说明,但这意味着总是会创建一个新对象。 通常你不需要担心这个。 但是,如果您的模型包含您设置的手动主键值,并且该值已存在于数据库中,则调用 create() 将失败并显示 IntegrityError,因为主键必须是唯一的. 如果您使用手动主键,请准备好处理异常。


get_or_create()

get_or_create(defaults=None, **kwargs)

一种使用给定 kwargs 查找对象的便捷方法(如果您的模型具有所有字段的默认值,则可能为空),如有必要,创建一个。

返回 (object, created) 的元组,其中 object 是检索或创建的对象,created 是指定是否创建新对象的布尔值。

这是为了防止在并行发出请求时创建重复的对象,并作为样板代码的快捷方式。 例如:

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

这里,对于并发请求,可能会多次尝试保存具有相同参数的 Person。 为了避免这种竞争条件,上面的例子可以使用 get_or_create() 重写,如下所示:

obj, created = Person.objects.get_or_create(
    first_name='John',
    last_name='Lennon',
    defaults={'birthday': date(1940, 10, 9)},
)

任何传递给 get_or_create() 的关键字参数 — 除外 一个称为 defaults 的可选参数 — 将在 get() 调用中使用。 如果找到对象,get_or_create() 返回该对象的元组和 False

警告

此方法是原子的,假设数据库强制关键字参数的唯一性(请参阅 uniqueunique_together)。 如果关键字参数中使用的字段没有唯一性约束,则对此方法的并发调用可能会导致插入具有相同参数的多行。


您可以通过将 get_or_create()filter() 链接起来并使用 Q 对象 来为检索到的对象指定更复杂的条件。 例如,检索 Robert 或 Bob Marley(如果存在),否则创建后者:

from django.db.models import Q

obj, created = Person.objects.filter(
    Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})

如果找到多个对象,get_or_create() 会引发 MultipleObjectsReturned。 如果没有找到一个对象,get_or_create()将实例化并保存一个新对象,返回一个新对象和True的元组。 新对象将根据以下算法大致创建:

params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()

在英语中,这意味着以任何不包含双下划线的非 'defaults' 关键字参数开头(这表示不精确的查找)。 然后添加 defaults 的内容,必要时覆盖任何键,并将结果用作模型类的关键字参数。 如果 defaults 中有任何可调用对象,请评估它们。 如上所述,这是所用算法的简化,但它包含所有相关细节。 内部实现具有比这更多的错误检查并处理一些额外的边缘条件; 如果您有兴趣,请阅读代码。

如果您有一个名为 defaults 的字段并希望将其用作 get_or_create() 中的精确查找,只需使用 'defaults__exact',如下所示:

Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})

当您使用手动指定的主键时,get_or_create() 方法具有与 create() 类似的错误行为。 如果需要创建对象并且密钥已经存在于数据库中,则会引发 IntegrityError

最后,谈谈在 Django 视图中使用 get_or_create()。 请确保仅在 POST 请求中使用它,除非您有充分的理由不这样做。 GET 请求不应对数据产生任何影响。 相反,只要对页面的请求对您的数据有副作用,就使用 POST。 有关更多信息,请参阅 HTTP 规范中的 安全方法

警告

您可以使用 get_or_create()ManyToManyField 属性和反向关系。 在这种情况下,您将限制该关系上下文内的查询。 如果您不始终如一地使用它,这可能会导致您出现一些完整性问题。

有以下型号:

class Chapter(models.Model):
    title = models.CharField(max_length=255, unique=True)

class Book(models.Model):
    title = models.CharField(max_length=256)
    chapters = models.ManyToManyField(Chapter)

您可以通过 Book 的章节字段使用 get_or_create(),但它只能在该书的上下文中获取:

>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError

之所以发生这种情况,是因为它试图通过“尤利西斯”一书获取或创建“第 1 章”,但它无法执行任何操作:该关系无法获取该章节,因为它与该书无关,但是它也不能创建它,因为 title 字段应该是唯一的。


update_or_create()

update_or_create(defaults=None, **kwargs)

一种使用给定 kwargs 更新对象的便捷方法,必要时创建一个新对象。 defaults 是用于更新对象的(字段,值)对的字典。 defaults 中的值可以是可调用的。

返回 (object, created) 元组,其中 object 是创建或更新的对象,created 是一个布尔值,指定是否创建了新对象。

update_or_create 方法尝试根据给定的 kwargs 从数据库中获取一个对象。 如果找到匹配项,它会更新 defaults 字典中传递的字段。

这意味着作为样板代码的快捷方式。 例如:

defaults = {'first_name': 'Bob'}
try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in defaults.items():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    new_values = {'first_name': 'John', 'last_name': 'Lennon'}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()

随着模型中字段数量的增加,这种模式变得非常笨拙。 上面的例子可以使用 update_or_create() 重写,如下所示:

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

有关如何解析 kwargs 中传递的名称的详细说明,请参阅 get_or_create()

如上文 get_or_create() 中所述,此方法容易出现竞争条件,如果未在数据库级别强制执行唯一性,则可能导致同时插入多行。

get_or_create()create(),如果您使用手动指定的主键并且需要创建一个对象但该键已经存在于数据库中,则 引发 IntegrityError


bulk_create()

bulk_create(objs, batch_size=None, ignore_conflicts=False)

此方法以高效的方式将提供的对象列表插入到数据库中(通常只有 1 个查询,无论有多少个对象):

>>> Entry.objects.bulk_create([
...     Entry(headline='This is a test'),
...     Entry(headline='This is only a test'),
... ])

不过这有一些注意事项:

  • 不会调用模型的save()方法,也不会发送pre_savepost_save信号。

  • 在多表继承的情况下,它不能与子模型一起工作。

  • 如果模型的主键是 AutoField,它不会像 save() 那样检索和设置主键属性,除非数据库后端支持它(当前是 PostgreSQL)。

  • 对于多对多的关系,它是行不通的。

  • 它将 objs 转换为一个列表,如果它是一个生成器,它会完全评估 objs。 强制转换允许检查所有对象,以便可以首先插入具有手动设置的主键的任何对象。 如果您想批量插入对象而不一次评估整个生成器,只要对象没有任何手动设置的主键,您就可以使用此技术:

    from itertools import islice
    
    batch_size = 100
    objs = (Entry(headline='Test %s' % i) for i in range(1000))
    while True:
        batch = list(islice(objs, batch_size))
        if not batch:
            break
        Entry.objects.bulk_create(batch, batch_size)

batch_size 参数控制在单个查询中创建的对象数量。 默认是批量创建所有对象,SQLite 除外,默认情况下每个查询最多使用 999 个变量。

在支持它的数据库上(除了 PostgreSQL < 9.5 和 Oracle),设置ignore_conflicts参数为True告诉数据库忽略插入失败约束(例如重复唯一值)的任何行的失败。 启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持它)。

2.2 版更改: 添加了 ignore_conflicts 参数。


bulk_update()

2.2 版中的新功能。


bulk_update(objs, fields, batch_size=None)

此方法有效地更新提供的模型实例上的给定字段,通常使用一个查询:

>>> objs = [
...    Entry.objects.create(headline='Entry 1'),
...    Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])

QuerySet.update() 用于保存更改,因此这比遍历模型列表并在每个模型上调用 save() 更有效,但它有一些警告:

  • 您无法更新模型的主键。
  • 不会调用每个模型的 save() 方法,并且不会发送 pre_savepost_save 信号。
  • 如果更新大量行中的大量列,则生成的 SQL 可能非常大。 通过指定合适的 batch_size 来避免这种情况。
  • 更新定义在多表继承祖先上的字段将给每个祖先带来额外的查询。
  • 如果 objs 包含重复项,则仅更新第一个。

batch_size 参数控制在单个查询中保存的对象数量。 默认是批量更新所有对象,SQLite 和 Oracle 除外,它们对查询中使用的变量数量有限制。


count()

count()

返回一个整数,表示数据库中与 QuerySet 匹配的对象数。

例子:

# Returns the total number of entries in the database.
Entry.objects.count()

# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()

count() 调用在幕后执行 SELECT COUNT(*),因此您应该始终使用 count(),而不是将所有记录加载到 Python 对象中并调用 len()结果(除非您无论如何都需要将对象加载到内存中,在这种情况下 len() 会更快)。

请注意,如果您想要 QuerySet 中的项目数并且还从中检索模型实例(例如,通过对其进行迭代),则使用 len(queryset) 可能更有效t 会导致像 count() 这样的额外数据库查询。


in_bulk()

in_bulk(id_list=None, field_name='pk')

获取字段值列表 (id_list) 和这些值的 field_name,并返回一个字典,将每个值映射到具有给定字段值的对象实例。 如果未提供 id_list,则返回查询集中的所有对象。 field_name 必须是唯一字段,默认为主键。

例子:

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}

如果你传递 in_bulk() 一个空列表,你会得到一个空字典。


iterator()

iterator(chunk_size=2000)

评估 QuerySet(通过执行查询)并返回一个迭代器(参见 PEP 234)。 QuerySet 通常在内部缓存其结果,以便重复评估不会导致额外的查询。 相比之下, iterator() 会直接读取结果,不会在 QuerySet 级别做任何缓存(在内部,默认迭代器调用 iterator() 并缓存返回值)。 对于返回大量只需要访问一次的对象的 QuerySet,这可以带来更好的性能并显着减少内存。

请注意,在已经计算过的 QuerySet 上使用 iterator() 将强制它再次计算,重复查询。

此外,使用 iterator() 会导致之前的 prefetch_related() 调用被忽略,因为这两个优化一起使用没有意义。

根据数据库后端,查询结果将被一次性加载或使用服务器端的游标从数据库中流转。

使用服务器端游标

Oracle 和 PostgreSQL 使用服务器端游标从数据库中流式传输结果,而无需将整个结果集加载到内存中。

Oracle 数据库驱动程序总是使用服务器端的游标。

对于服务器端游标,chunk_size 参数指定要在数据库驱动程序级别缓存的结果数。 获取更大的块会减少数据库驱动程序和数据库之间的往返次数,但会消耗内存。

在 PostgreSQL 上,服务器端游标只会在以下情况下使用 :设置:`DISABLE_SERVER_SIDE_CURSORS ` 设置是False . 如果您使用以事务池模式配置的连接池,请阅读 事务池和服务器端游标 。 禁用服务器端游标时,行为与不支持服务器端游标的数据库相同。


没有服务器端游标

MySQL 不支持流式结果,因此 Python 数据库驱动程序将整个结果集加载到内存中。 然后,数据库适配器使用 PEP 249 中定义的 fetchmany() 方法将结果集转换为 Python 行对象。

SQLite 可以使用 fetchmany() 批量获取结果,但由于 SQLite 不提供连接内查询之间的隔离,因此在写入被迭代的表时要小心。 有关详细信息,请参阅使用 QuerySet.iterator() 时的 隔离。

chunk_size 参数控制 Django 从数据库驱动程序中检索的批次大小。 较大的批处理减少了与数据库驱动程序通信的开销,但代价是内存消耗略有增加。

chunk_size 的默认值,2000,来自 psycopg 邮件列表 上的 计算:

假设行数为 10-20 列,文字数据和数字数据混合,2000 要取不到 100KB 的数据,这似乎是一个很好的折中方案,在传输的行数和提前退出循环时丢弃的数据之间。


2.2 版更改: 添加了对 SQLite 上的结果流的支持。


latest()

latest(*fields)

根据给定的字段,返回表中最新的对象。

本示例根据 pub_date 字段返回表中最新的 Entry

Entry.objects.latest('pub_date')

您还可以根据多个字段选择最新的。 例如,当两个条目具有相同的 pub_date 时,选择 Entry 与最早的 expire_date

Entry.objects.latest('pub_date', '-expire_date')

'-expire_date'中的负号表示按降序expire_date进行排序。 由于latest()得到最后的结果,所以选择了expire_date最早的Entry

如果您模型的 Meta 指定 get_latest_by,则您可以省略 earliest()latest() 的任何参数。 默认情况下将使用 get_latest_by 中指定的字段。

get()earliest()latest() 引发 DoesNotExist 如果没有具有给定参数的对象。

请注意,earliest()latest() 的存在纯粹是为了方便和可读性。

earliest()latest() 可能返回具有空日期的实例。

由于排序委托给数据库,如果您使用不同的数据库,则允许空值的字段的结果可能会以不同的方式排序。 例如,PostgreSQL 和 MySQL 对空值进行排序,就好像它们高于非空值一样,而 SQLite 则相反。

您可能希望过滤掉空值:

Entry.objects.filter(pub_date__isnull=False).latest('pub_date')

earliest()

earliest(*fields)

除了方向改变外,其他方式与 latest() 类似。


first()

first()

返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。 如果 QuerySet 没有定义排序,那么查询集会自动按主键排序。 这可能会影响聚合结果,如 与默认排序或 order_by() 的交互中所述。

例子:

p = Article.objects.order_by('title', 'pub_date').first()

注意 first() 是一个方便的方法,下面的代码示例相当于上面的例子:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

last()

last()

first() 一样工作,但返回查询集中的最后一个对象。


aggregate()

aggregate(*args, **kwargs)

返回在 QuerySet 上计算的聚合值(平均值、总和等)的字典。 aggregate() 的每个参数指定一个值,该值将包含在返回的字典中。

Django 提供的聚合函数在下面的 Aggregation Functions 中描述。 由于聚合也是 查询表达式 ,您可以将聚合与其他聚合或值组合以创建复杂的聚合。

使用关键字参数指定的聚合将使用关键字作为注释的名称。 匿名参数将根据聚合函数的名称和正在聚合的模型字段为它们生成一个名称。 复杂聚合不能使用匿名参数,必须将关键字参数指定为别名。

例如,当您处理博客条目时,您可能想知道贡献了博客条目的作者数量:

>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}

通过使用关键字参数指定聚合函数,您可以控制返回的聚合值的名称:

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}

有关聚合的深入讨论,请参阅 有关聚合的主题指南


exists()

exists()

如果 QuerySet 包含任何结果,则返回 True,否则返回 False。 这试图以最简单和最快的方式执行查询,但它 确实 执行与普通 QuerySet 查询几乎相同的查询。

exists() 可用于与 QuerySet 中的对象成员资格和 QuerySet 中任何对象的存在相关的搜索,特别是在大 查询集

查找模型是否具有唯一字段的最有效方法(例如 primary_key) 是 QuerySet 的成员是:

entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
    print("Entry contained in queryset")

这将比以下需要评估和迭代整个查询集的更快:

if entry in some_queryset:
   print("Entry contained in QuerySet")

并查找查询集是否包含任何项目:

if some_queryset.exists():
    print("There is at least one object in some_queryset")

这将比:

if some_queryset:
    print("There is at least one object in some_queryset")

……但不是很大(因此需要一个大的查询集来提高效率)。

此外,如果 some_queryset 尚未被评估,但您知道它会在某个时刻被评估,那么使用 some_queryset.exists() 将完成更多的整体工作(一个存在检查的查询加上一个额外的一到稍后检索结果),而不是简单地使用 bool(some_queryset),后者检索结果,然后检查是否有任何返回。


update()

update(**kwargs)

对指定的字段执行 SQL 更新查询,并返回匹配的行数(如果有些行已经有了新的值,则可能不等于更新的行数)。

例如,要关闭 2010 年发布的所有博客条目的评论,您可以执行以下操作:

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)

(这假设您的 Entry 模型具有字段 pub_datecomments_on。)

您可以更新多个字段 - 数量没有限制。 例如,这里我们更新 comments_onheadline 字段:

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')

update() 方法是即时应用的,更新的 QuerySet 唯一的限制是它只能更新模型主表中的列,不能更新相关模型。 你不能这样做,例如:

>>> Entry.objects.update(blog__name='foo') # Won't work!

不过,仍然可以根据相关字段进行过滤:

>>> Entry.objects.filter(blog__id=1).update(comments_on=True)

您不能在已获取切片或无法再过滤的 QuerySet 上调用 update()

update() 方法返回受影响的行数:

>>> Entry.objects.filter(id=64).update(comments_on=True)
1

>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132

如果你只是更新一条记录并且不需要对模型对象做任何事情,最有效的方法是调用 update(),而不是将模型对象加载到内存中。 例如,不要这样做:

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

…做这个:

Entry.objects.filter(id=10).update(comments_on=False)

使用 update() 还可以防止竞争条件,即在加载对象和调用 save() 之间的短时间内,数据库中的某些内容可能会发生变化。

最后,意识到 update() 在 SQL 级别进行更新,因此不会在您的模型上调用任何 save() 方法,也不会发出 pre_save 或 [ X169X]post_save 信号(这是调用 Model.save() 的结果)。 如果要更新具有自定义 save() 方法的模型的一堆记录,请遍历它们并调用 save(),如下所示:

for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()

delete()

delete()

QuerySet 中的所有行执行 SQL 删除查询,并返回删除的对象数和包含每种对象类型删除数的字典。

delete() 立即应用。 您不能在已获取切片或无法再过滤的 QuerySet 上调用 delete()

例如,要删除特定博客中的所有条目:

>>> b = Blog.objects.get(pk=1)

# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})

默认情况下,Django 的 ForeignKey 模拟 SQL 约束 ON DELETE CASCADE — 换句话说,任何外键指向要删除的对象的对象都将被删除。 例如:

>>> blogs = Blog.objects.all()

# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})

此级联行为可通过 ForeignKeyon_delete 参数进行自定义。

delete() 方法执行批量删除,并且不会在您的模型上调用任何 delete() 方法。 但是,它确实为所有已删除的对象(包括级联删除)发出 pre_deletepost_delete 信号。

Django 需要将对象提取到内存中以发送信号和处理级联。 但是,如果没有级联和信号,那么 Django 可能会采用快速路径并删除对象而不提取到内存中。 对于大型删除,这会导致显着减少内存使用。 执行查询的数量也可以减少。

设置为 on_delete DO_NOTHING 的外键不会阻止在删除时采用快速路径。

需要注意的是,对象删除中产生的查询是一个实现细节,可能会发生变化。


as_manager()

classmethod as_manager()

返回 Manager 实例和 QuerySet 方法副本的类方法。 有关更多详细信息,请参阅 使用 QuerySet 方法创建管理器


explain()

2.1 版中的新功能。


explain(format=None, **options)

返回 QuerySet 的执行计划的字符串,它详细说明了数据库将如何执行查询,包括将使用的任何索引或连接。 了解这些细节可能会帮助您提高慢查询的性能。

例如,当使用 PostgreSQL 时:

>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog  (cost=0.00..35.50 rows=10 width=12)
  Filter: (title = 'My Blog'::bpchar)

不同数据库之间的输出有很大的不同。

explain() 被除 Oracle 之外的所有内置数据库后端支持,因为那里的实现并不简单。

format 参数改变了数据库默认的输出格式,通常是基于文本的。 PostgreSQL 支持 'TEXT''JSON''YAML''XML'。 MySQL 支持 'TEXT'(也称为 'TRADITIONAL')和 'JSON'

某些数据库接受可以返回有关查询的更多信息的标志。 将这些标志作为关键字参数传递。 例如,当使用 PostgreSQL 时:

>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True))
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms

在某些数据库上,标志可能会导致执行查询,这可能会对您的数据库产生不利影响。 例如,如果有触发器或函数被调用,PostgreSQL 的 ANALYZE 标志可能会导致数据更改,即使对于 SELECT 查询也是如此。


Field 查找

字段查找是您指定 SQL WHERE 子句内容的方式。 它们被指定为 QuerySet 方法 filter()exclude()get() 的关键字参数。

有关介绍,请参阅 模型和数据库查询文档

下面列出了 Django 的内置查找。 也可以为模型字段编写 自定义查找

为了方便,当没有提供查找类型时(如 Entry.objects.get(id=14)),查找类型假定为 :lookup:`exact`

exact

完全符合。 如果为比较提供的值是 None,它将被解释为 SQL NULL(有关更多详细信息,请参阅 :lookup:`isnull`)。

例子:

Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)

SQL 等效项:

SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;

MySQL 比较

在 MySQL 中,数据库表的“排序规则”设置确定 exact 比较是否区分大小写。 这是一个数据库设置, 不是 一个 Django 设置。 可以将 MySQL 表配置为使用区分大小写的比较,但需要进行一些权衡。 有关这方面的更多信息,请参阅 数据库 文档中的 整理部分


iexact

不区分大小写的精确匹配。 如果为比较提供的值是 None,它将被解释为 SQL NULL(有关更多详细信息,请参阅 :lookup:`isnull`)。

例子:

Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)

SQL 等效项:

SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;

请注意,第一个查询将匹配 'Beatles Blog''beatles blog''BeAtLes BLoG' 等。

SQLite 用户

使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 数据库注释 。 SQLite 不会对非 ASCII 字符串进行不区分大小写的匹配。


contains

区分大小写的包含测试。

例子:

Entry.objects.get(headline__contains='Lennon')

SQL 等价于:

SELECT ... WHERE headline LIKE '%Lennon%';

请注意,这将匹配标题 'Lennon honored today' 但不匹配 'lennon honored today'

SQLite 用户

SQLite 不支持区分大小写的 LIKE 语句; contains 的作用类似于 SQLite 的 icontains。 有关更多信息,请参阅 数据库注释


icontains

不区分大小写的包含测试。

例子:

Entry.objects.get(headline__icontains='Lennon')

SQL 等价于:

SELECT ... WHERE headline ILIKE '%Lennon%';

SQLite 用户

使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 数据库注释


in

在给定的迭代中; 通常是列表、元组或查询集。 这不是一个常见的用例,但接受字符串(可迭代)。

例子:

Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')

SQL 等效项:

SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');

您还可以使用查询集来动态评估值列表,而不是提供文字值列表:

inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)

此查询集将作为子选择语句进行评估:

SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')

如果您将 values()values_list() 产生的 QuerySet 作为值传递给 __in 查找,则需要确保只提取一个字段结果。 例如,这将起作用(过滤博客名称):

inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
entries = Entry.objects.filter(blog__name__in=inner_qs)

此示例将引发异常,因为内部查询试图提取两个字段值,其中只需要一个:

# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
entries = Entry.objects.filter(blog__name__in=inner_qs)

性能注意事项

谨慎使用嵌套查询并了解您的数据库服务器的性能特征(如果有疑问,请进行基准测试!)。 一些数据库后端,尤其是 MySQL,不能很好地优化嵌套查询。 在这些情况下,提取值列表然后将其传递到第二个查询中会更有效。 也就是说,执行两个查询而不是一个:

values = Blog.objects.filter(
        name__contains='Cheddar').values_list('pk', flat=True)
entries = Entry.objects.filter(blog__in=list(values))

请注意博客 QuerySet 周围的 list() 调用以强制执行第一个查询。 没有它,将执行嵌套查询,因为 QuerySets 是惰性的


gt

大于。

例子:

Entry.objects.filter(id__gt=4)

SQL 等价于:

SELECT ... WHERE id > 4;

gte

大于等于。


lt

小于。


lte

小于等于


startswith

区分大小写的开头为。

例子:

Entry.objects.filter(headline__startswith='Lennon')

SQL 等价于:

SELECT ... WHERE headline LIKE 'Lennon%';

SQLite 不支持区分大小写的 LIKE 语句; startswith 的作用类似于 SQLite 的 istartswith


istartswith

不区分大小写的开头为。

例子:

Entry.objects.filter(headline__istartswith='Lennon')

SQL 等价于:

SELECT ... WHERE headline ILIKE 'Lennon%';

SQLite 用户

使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 数据库注释


endswith

区分大小写的结尾为。

例子:

Entry.objects.filter(headline__endswith='Lennon')

SQL 等价于:

SELECT ... WHERE headline LIKE '%Lennon';

SQLite 用户

SQLite 不支持区分大小写的 LIKE 语句; endswith 的作用类似于 SQLite 的 iendswith。 有关更多信息,请参阅 数据库注释 文档。


iendswith

不区分大小写的结尾为。

例子:

Entry.objects.filter(headline__iendswith='Lennon')

SQL 等价于:

SELECT ... WHERE headline ILIKE '%Lennon'

SQLite 用户

使用 SQLite 后端和非 ASCII 字符串时,请记住有关字符串比较的 数据库注释


range

范围测试(含)。

例子:

import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))

SQL 等价于:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

在 SQL 中可以使用 BETWEEN 的任何地方都可以使用 range — 用于日期、数字甚至字符。

警告

用日期过滤 DateTimeField 将不包括最后一天的项目,因为边界被解释为“给定日期的上午 0 点”。 如果 pub_date 是一个 DateTimeField,上面的表达式会变成这个 SQL:

SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';

一般来说,您不能混合日期和日期时间。


date

对于日期时间字段,将值转换为日期。 允许链接额外的字段查找。 获取日期值。

例子:

Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。

:setting:`USE_TZ`True 时,字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


year

对于日期和日期时间字段,精确的年份匹配。 允许链接额外的字段查找。 取整数年。

例子:

Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

SQL 等价于:

SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


iso_year

2.2 版中的新功能。


对于日期和日期时间字段,精确的 ISO 8601 周编号年份匹配。 允许链接额外的字段查找。 取整数年。

例子:

Entry.objects.filter(pub_date__iso_year=2005)
Entry.objects.filter(pub_date__iso_year__gte=2005)

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


month

对于日期和日期时间字段,精确的月份匹配。 允许链接额外的字段查找。 取整数 1(一月)到 12(十二月)。

例子:

Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)

SQL 等价于:

SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


day

对于日期和日期时间字段,精确的日期匹配。 允许链接额外的字段查找。 需要整数天。

例子:

Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

SQL 等价于:

SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';

(确切的 SQL 语法因每个数据库引擎而异)。

请注意,这将匹配任何带有 pub_date 的月份第三天的记录,如 1 月 3 日,7 月 3 日等。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


week

对于日期和日期时间字段,根据 ISO-8601 返回周数(1-52 或 53),即周从星期一开始,第一周包含年度的第一个星期四。

例子:

Entry.objects.filter(pub_date__week=52)
Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)

(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


week_day

对于日期和日期时间字段,“星期几”匹配。 允许链接额外的字段查找。

从 1(星期日)到 7(星期六)取一个整数值,代表一周的一天。

例子:

Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)

(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。

请注意,这将匹配任何具有 pub_date 位于星期一(一周的第 2 天)的记录,而不管它发生在哪个月份或年份。 工作日被索引,第 1 天是星期日,第 7 天是星期六。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


quarter

对于日期和日期时间字段,“一年中的一个季度”匹配。 允许链接额外的字段查找。 取一个 1 到 4 之间的整数值,表示一年中的一个季度。

检索第二季度(4 月 1 日至 6 月 30 日)条目的示例:

Entry.objects.filter(pub_date__quarter=2)

(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


time

对于日期时间字段,将值转换为时间。 允许链接额外的字段查找。 采用 datetime.time 值。

例子:

Entry.objects.filter(pub_date__time=datetime.time(14, 30))
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))

(由于不同的数据库引擎对相关查询的实现各不相同,因此本次查询不包含等效的 SQL 代码片段)。

:setting:`USE_TZ`True 时,字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


hour

对于日期时间和时间字段,精确的小时匹配。 允许链接额外的字段查找。 取 0 到 23 之间的整数。

例子:

Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)

SQL 等价于:

SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


minute

对于日期时间和时间字段,精确的分钟匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。

例子:

Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)

SQL 等价于:

SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


second

对于日期时间和时间字段,精确的第二个匹配。 允许链接额外的字段查找。 取 0 到 59 之间的整数。

例子:

Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)

SQL 等价于:

SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';

(确切的 SQL 语法因每个数据库引擎而异)。

:setting:`USE_TZ`True 时,日期时间字段在过滤前转换为当前时区。 这需要数据库 中的 时区定义。


isnull

采用 TrueFalse,分别对应于 IS NULLIS NOT NULL 的 SQL 查询。

例子:

Entry.objects.filter(pub_date__isnull=True)

SQL 等价于:

SELECT ... WHERE pub_date IS NULL;

regex

区分大小写的正则表达式匹配。

正则表达式语法是正在使用的数据库后端的语法。 对于没有内置正则表达式支持的 SQLite,此功能由(Python)用户定义的 REGEXP 函数提供,因此正则表达式语法是 Python 的 re 模块的语法。

例子:

Entry.objects.get(title__regex=r'^(An?|The) +')

SQL 等效项:

SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle

SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite

建议使用原始字符串(例如,r'foo' 而不是 'foo')来传递正则表达式语法。


iregex

不区分大小写的正则表达式匹配。

例子:

Entry.objects.get(title__iregex=r'^(an?|the) +')

SQL 等效项:

SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL

SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle

SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite

聚合函数

Django 在 django.db.models 模块中提供了以下聚合函数。 有关如何使用这些聚合函数的详细信息,请参阅 聚合主题指南 。 请参阅 Aggregate 文档以了解如何创建聚合。

警告

SQLite 无法处理开箱即用的日期/时间字段的聚合。 这是因为 SQLite 中没有原生日期/时间字段,而 Django 目前使用文本字段模拟这些功能。 尝试在 SQLite 中的日期/时间字段上使用聚合将引发 NotImplementedError


注解

与空 QuerySet 一起使用时,聚合函数返回 None。 例如,如果 QuerySet 不包含任何条目,则 Sum 聚合函数返回 None 而不是 0。 一个例外是 Count,如果 QuerySet 为空,它会返回 0


所有聚合体都有以下共同的参数:

expressions

引用模型字段的字符串,或 查询表达式


output_field

一个可选参数,表示返回值的 模型字段

注解

在组合多个字段类型时,如果所有字段类型相同,Django 只能确定 output_field。 否则,您必须自己提供 output_field


filter

一个可选的 Q 对象 ,用于过滤聚合的行。

有关示例用法,请参阅 条件聚合注释过滤


**extra

关键字参数,可以为聚合生成的 SQL 提供额外的上下文。


Avg

class Avg(expression, output_field=None, filter=None, **extra)
返回给定表达式的平均值,除非您指定不同的 output_field,否则该平均值必须是数字。
  • 默认别名:<field>__avg
  • 返回类型:float 如果输入是 int,否则与输入字段相同,或者 output_field 如果提供


Count

class Count(expression, distinct=False, filter=None, **extra)

返回通过提供的表达式关联的对象数量。

  • 默认别名:<field>__count

  • 返回类型:int

包含一个可选参数:

distinct

如果为 distinct=True,则计数将仅包括唯一实例。 这是 COUNT(DISTINCT <field>) 的 SQL 等价物。 默认值为 False


Max

class Max(expression, output_field=None, filter=None, **extra)
返回给定表达式的最大值。
  • 默认别名:<field>__max
  • 返回类型:与输入字段相同,或 output_field(如果提供)


Min

class Min(expression, output_field=None, filter=None, **extra)
返回给定表达式的最小值。
  • 默认别名:<field>__min
  • 返回类型:与输入字段相同,或 output_field(如果提供)


StdDev

class StdDev(expression, output_field=None, sample=False, filter=None, **extra)

返回给定表达式中数据的标准差。

  • 默认别名:<field>__stddev

  • 返回类型:float 如果输入是 int,否则与输入字段相同,或者 output_field 如果提供

包含一个可选参数:

sample

默认情况下,StdDev 返回总体标准差。 但是,如果sample=True,则返回值将是样本标准差。

在 2.2 版更改:添加了 SQLite 支持。


Sum

class Sum(expression, output_field=None, filter=None, **extra)
计算给定表达式的所有值的总和。
  • 默认别名:<field>__sum
  • 返回类型:与输入字段相同,或 output_field(如果提供)


Variance

class Variance(expression, output_field=None, sample=False, filter=None, **extra)

返回给定表达式中数据的方差。

  • 默认别名:<field>__variance

  • 返回类型:float 如果输入是 int,否则与输入字段相同,或者 output_field 如果提供

包含一个可选参数:

sample

默认情况下,Variance 返回总体方差。 但是,如果sample=True,返回值将是样本方差。

在 2.2 版更改:添加了 SQLite 支持。