QuerySet
API 参考¶
本文档描述了 QuerySet
API 的详细信息。它建立在 模型 和 数据库查询 指南中提供的材料基础上,因此您可能希望在阅读本文档之前阅读并理解这些文档。
在整个参考中,我们将使用 示例博客模型,这些模型在 数据库查询指南 中进行了介绍。
何时评估 QuerySet
¶
在内部,QuerySet
可以被构造、过滤、切片,并且通常可以在不实际访问数据库的情况下传递。只有当您执行某些操作来评估 QuerySet 时,才会真正发生数据库活动。
您可以通过以下方式评估 QuerySet
迭代。
QuerySet
是可迭代的,并且在您第一次迭代它时会执行其数据库查询。例如,这将打印数据库中所有条目的标题for e in Entry.objects.all(): print(e.headline)
注意:如果您只想确定是否存在至少一个结果,请不要使用此方法。使用
exists()
效率更高。异步迭代。
QuerySet
也可以使用async for
进行迭代async for e in Entry.objects.all(): results.append(e)
QuerySet 的同步和异步迭代器共享相同的底层缓存。
切片。如 限制 QuerySet 中所述,
QuerySet
可以使用 Python 的数组切片语法进行切片。切片未评估的QuerySet
通常会返回另一个未评估的QuerySet
,但如果使用切片语法的“步长”参数,Django 将执行数据库查询并返回列表。切片已评估的QuerySet
也返回列表。另请注意,即使切片未评估的
QuerySet
会返回另一个未评估的QuerySet
,但进一步修改它(例如,添加更多过滤器或修改排序)是不允许的,因为这无法很好地转换为 SQL,而且也没有明确的含义。腌制/缓存。有关腌制 QuerySet 时涉及的内容的详细信息,请参阅下一节。对于本节的目的,重要的是结果是从数据库中读取的。
repr()。当您对
QuerySet
调用repr()
时,会评估它。这是为了在 Python 交互式解释器中方便起见,以便您在以交互方式使用 API 时可以立即看到结果。len()。当您对
QuerySet
调用len()
时,会评估它。这(正如您可能期望的那样)返回结果列表的长度。注意:如果您只需要确定集合中的记录数量(并且不需要实际的对象),那么使用 SQL 的
SELECT COUNT(*)
在数据库级别处理计数的效率更高。Django 提供了count()
方法,正是出于这个原因。list()。通过对
QuerySet
调用list()
来强制评估它。例如entry_list = list(Entry.objects.all())
bool()。在布尔上下文中测试
QuerySet
,例如使用bool()
、or
、and
或if
语句,将导致查询被执行。如果至少存在一个结果,则QuerySet
为True
,否则为False
。例如if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")
注意:如果您只想确定是否存在至少一个结果(并且不需要实际的对象),那么使用
exists()
效率更高。
腌制 QuerySet
¶
如果您 pickle
一个 QuerySet
,这将强制所有结果在腌制之前加载到内存中。腌制通常用作缓存的前兆,并且当缓存的 QuerySet 重新加载时,您希望结果已经存在并准备使用(从数据库读取可能需要一些时间,从而破坏缓存的目的)。这意味着当您取消腌制 QuerySet
时,它包含腌制时的那一刻的结果,而不是当前数据库中的结果。
如果您只想腌制必要的信息以便稍后从数据库中重新创建 QuerySet
,请腌制 QuerySet
的 query
属性。然后,您可以使用如下代码重新创建原始 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 的一部分。但是,如本文档所述,腌制和取消腌制属性的内容是安全且完全受支持的。
对 QuerySet.values_list()
的限制
如果您使用腌制的 query
属性重新创建 QuerySet.values_list()
,它将被转换为 QuerySet.values()
>>> import pickle
>>> qs = Blog.objects.values_list("id", "name")
>>> qs
<QuerySet [(1, 'Beatles Blog')]>
>>> reloaded_qs = Blog.objects.all()
>>> reloaded_qs.query = pickle.loads(pickle.dumps(qs.query))
>>> reloaded_qs
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
QuerySet
API¶
以下是 QuerySet
的正式声明
- class QuerySet(model=None, query=None, using=None, hints=None)[source]¶
通常,当您与
QuerySet
交互时,您将通过 链接过滤器 来使用它。为了使这能够工作,大多数QuerySet
方法都会返回新的 QuerySet。这些方法将在本节后面详细介绍。QuerySet
类具有以下可用于内省的公共属性- ordered[source]¶
如果
QuerySet
有序(即包含order_by()
子句或模型上的默认排序),则返回True
;否则返回False
。
注意
QuerySet
的query
参数是为了让专门的查询子类能够重建内部查询状态。该参数的值是该查询状态的不透明表示,不是公共 API 的一部分。
返回新 QuerySet
的方法¶
Django 提供了一系列 QuerySet
细化方法,这些方法可以修改 QuerySet
返回的结果类型或其 SQL 查询的执行方式。
注意
这些方法不会运行数据库查询,因此它们**可以在异步代码中安全运行**,并且没有单独的异步版本。
filter()
¶
- filter(*args, **kwargs)¶
返回一个新的 QuerySet
,其中包含与给定查找参数匹配的对象。
查找参数(**kwargs
)应采用下面字段查找中描述的格式。多个参数在底层 SQL 语句中通过 AND
连接。
如果您需要执行更复杂的查询(例如,包含 OR
语句的查询),可以使用 Q 对象
(*args
)。
exclude()
¶
- exclude(*args, **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 或 headline
为“Hello”的条目。
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 对象
(*args
)。
annotate()
¶
- annotate(*args, **kwargs)¶
使用提供的 查询表达式 列表为 QuerySet
中的每个对象添加注释。表达式可以是简单值、模型(或任何相关模型)上的字段引用,或者是在与 QuerySet
中的对象相关的对象上计算的聚合表达式(平均值、总和等)。
annotate()
的每个参数都是一个注释,将添加到返回的 QuerySet
中的每个对象上。
Django 提供的聚合函数在下面聚合函数中进行了描述。
使用关键字参数指定的注释将使用关键字作为注释的别名。匿名参数将根据聚合函数的名称和正在聚合的模型字段生成别名。只有引用单个字段的聚合表达式才能成为匿名参数。其他所有内容都必须是关键字参数。
例如,如果您正在操作博客列表,您可能希望确定每个博客中发布了多少条目。
>>> 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
有关聚合的深入讨论,请参阅聚合主题指南。
alias()
¶
- alias(*args, **kwargs)¶
与 annotate()
相同,但它不是为 QuerySet
中的对象添加注释,而是保存表达式以便以后与其他 QuerySet
方法一起重用。当不需要表达式的结果本身但将其用于过滤、排序或作为复杂表达式的组成部分时,这很有用。不选择未使用的值会减少数据库的冗余工作,从而提高性能。
例如,如果您想查找条目数超过 5 个的博客,但对确切的条目数不感兴趣,您可以这样做。
>>> from django.db.models import Count
>>> blogs = Blog.objects.alias(entries=Count("entry")).filter(entries__gt=5)
alias()
可以与 annotate()
、exclude()
、filter()
、order_by()
和 update()
一起使用。要将带别名的表达式与其他方法(例如 aggregate()
)一起使用,您必须将其提升为注释。
Blog.objects.alias(entries=Count("entry")).annotate(
entries=F("entries"),
).aggregate(Sum("entries"))
filter()
和 order_by()
可以直接使用表达式,但表达式构建和使用通常不会发生在同一个地方(例如,QuerySet
方法创建表达式,以便以后在视图中使用)。alias()
允许增量构建复杂表达式,可能跨越多个方法和模块,通过其别名引用表达式部分,并且仅对最终结果使用 annotate()
。
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")
如果Blog
具有ordering = ['name']
,则第一个查询集将与以下相同
Entry.objects.order_by("blog__name")
您还可以通过调用表达式上的asc()
或desc()
来按查询表达式排序
Entry.objects.order_by(Coalesce("summary", "headline").desc())
asc()
和desc()
具有参数(nulls_first
和nulls_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
可能有多个排序数据;每个具有多个children
的Event
将被多次返回到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)¶
返回一个新的QuerySet
,该查询集在其 SQL 查询中使用SELECT DISTINCT
。这将消除查询结果中的重复行。
默认情况下,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()
调用,数据库在确定哪些行是不同的时会比较每一行中的每个字段。对于指定字段名称的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'}]>
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()
并传入字段名称时,你可以传入foo
或foo_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()
调用都会忽略其额外选择的字段。组合转换和聚合需要使用两个
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}]>
当你只知道你需要使用少量可用字段中的值并且不需要模型实例对象的功能时,它很有用。仅选择你需要使用的字段效率更高。
最后,请注意,你可以在values()
调用之后调用filter()
、order_by()
等,这意味着这两个调用是相同的
Blog.objects.values().order_by("id")
Blog.objects.order_by("id").values()
Django 的开发者更喜欢先放置所有影响 SQL 的方法,然后(可选)放置任何影响输出的方法(如values()
),但这并不重要。这是你真正炫耀你个人风格的机会。
你还可以通过OneToOneField
、ForeignKey
和ManyToManyField
属性引用相关模型上的字段
>>> Blog.objects.values("name", "entry__headline")
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
警告
因为ManyToManyField
属性和反向关系可以有多个相关行,包括这些可以对结果集的大小产生倍增效应。如果你在values()
查询中包含多个此类字段,则这种情况尤其明显,在这种情况下,将返回所有可能的组合。
SQLite 上JSONField
的特殊值
由于 SQLite 上JSON_EXTRACT
和JSON_TYPE
SQL 函数的实现方式以及缺少BOOLEAN
数据类型,values()
将返回True
、False
和None
,而不是JSONField
键转换的"true"
、"false"
和"null"
字符串。
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
,则表示返回的结果是单个值,而不是 1 元组。一个例子应该能让区别更清楚
>>> 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,)]>
SQLite 上JSONField
的特殊值
由于 JSON_EXTRACT
和 JSON_TYPE
SQL 函数在 SQLite 上的实现方式以及缺少 BOOLEAN
数据类型,values_list()
将返回 True
、False
和 None
,而不是 "true"
、"false"
和 "null"
字符串,用于 JSONField
键转换。
dates()
¶
- dates(field, kind, order='ASC')¶
返回一个 QuerySet
,该 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)]
datetimes()
¶
- datetimes(field_name, kind, order='ASC', tzinfo=None)¶
返回一个 QuerySet
,该 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 使用 当前时区。当 USE_TZ
为 False
时,它没有效果。
注意
此函数在数据库中直接执行时区转换。因此,您的数据库必须能够解释 tzinfo.tzname(None)
的值。这转化为以下要求
SQLite:无需任何要求。转换在 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
。只要所有QuerySet
中的SELECT
列表相同(至少类型相同,名称只要类型顺序相同则无关紧要),传递不同的模型就可以工作。在这种情况下,必须在应用于结果QuerySet
的QuerySet
方法中使用第一个QuerySet
的列名。例如
>>> qs1 = Author.objects.values_list("name")
>>> qs2 = Entry.objects.values_list("headline")
>>> qs1.union(qs2).order_by("name")
此外,只有LIMIT
、OFFSET
、COUNT(*)
、ORDER BY
以及指定列(即切片、count()
、exists()
、order_by()
和values()
/values_list()
)允许用于结果QuerySet
。此外,数据库对组合查询中允许的操作有限制。例如,大多数数据库不允许在组合查询中使用LIMIT
或OFFSET
。
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 原则,因此您应尽可能避免它们。
指定 params
、select
、where
或 tables
中的一个或多个。没有参数是必需的,但您应该至少使用其中一个。
select
select
参数允许您在SELECT
子句中添加额外的字段。它应该是一个字典,将属性名称映射到用于计算该属性的 SQL 子句。示例
Entry.objects.extra(select={"is_recent": "pub_date > '2006-01-01'"})
因此,每个
Entry
对象将具有一个额外的属性is_recent
,一个布尔值,表示该条目的pub_date
是否大于 2006 年 1 月 1 日。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
子句中不是必需的。在某些罕见情况下,您可能希望将参数传递到
extra(select=...)
中的 SQL 片段。为此,请使用select_params
参数。例如,这将起作用
Blog.objects.extra( select={"a": "%s", "b": "%s"}, select_params=("one", "two"), )
如果需要在选择字符串内部使用文字
%s
,请使用序列%%s
。where
/tables
您可以通过使用
where
定义显式 SQLWHERE
子句——也许是为了执行非显式联接。您可以通过使用tables
手动将表添加到 SQLFROM
子句。where
和tables
都接受字符串列表。所有where
参数都与任何其他搜索条件进行“AND”运算。示例
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()
包含的一些新字段或表来对结果 QuerySet 进行排序,请使用extra()
的order_by
参数并传入一个字符串序列。这些字符串可以是模型字段(如 QuerySet 上的普通order_by()
方法),格式为table_name.column_name
,或者你在extra()
的select
参数中指定的列的别名。例如
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 对象。如果你在某些情况下使用 QuerySet 的结果,而你最初获取数据时不知道是否需要这些特定字段,你可以告诉 Django 不要从数据库中检索它们。
这是通过将不需要加载的字段名称传递给 defer()
来完成的。
Entry.objects.defer("headline", "body")
具有延迟字段的 QuerySet 仍然会返回模型实例。如果你访问某个延迟字段,它将从数据库中检索(一次一个,而不是一次检索所有延迟字段)。
注意
延迟字段不会像这样从异步代码中延迟加载。相反,你将得到一个 SynchronousOnlyOperation
异常。如果你正在编写异步代码,你不应该尝试访问任何你 defer()
的字段。
你可以多次调用 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()
)并包含聚合中的参数(例如,使用 annotate()
的结果)是没有意义的:这样做会导致异常。聚合值将始终被获取到结果 QuerySet 中。
注意
defer()
方法(及其同类 only()
,如下所示)仅适用于高级用例。它们提供了一种优化,用于当你仔细分析了你的查询并完全理解你需要的信息,并且已经衡量了返回你需要的字段和模型的完整字段集之间的差异是否显著时。
即使你认为你处于高级用例场景中,**也只在查询集加载时无法确定是否需要额外字段时才使用** defer()
。如果你经常加载和使用特定数据子集,你可以做出的最佳选择是规范化你的模型并将未加载的数据放入单独的模型(和数据库表)中。如果出于某种原因这些列**必须**保留在一个表中,请创建一个模型,其中 Meta.managed = False
(参见 managed attribute
文档),其中只包含你通常需要加载的字段,并在你可能调用 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.defer("f2")
如果在非托管模型中需要复制许多字段,最好创建一个包含共享字段的抽象模型,然后让非托管模型和托管模型继承自该抽象模型。
only()
¶
- only(*fields)¶
only()
方法本质上与 defer()
相反。当评估 QuerySet 时,只有传递给此方法且尚未指定为延迟的字段会被立即加载。
如果你有一个模型,其中几乎所有字段都需要延迟加载,则使用 only()
指定互补的字段集可以使代码更简单。
假设你有一个具有字段 name
、age
和 biography
的模型。以下两个 QuerySet 在延迟字段方面是相同的。
Person.objects.defer("age", "biography")
Person.objects.only("name")
每当你调用 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 immediately.
Entry.objects.defer("body").only("headline", "body")
defer()
文档中的注意事项也适用于 only()
。请谨慎使用,并在尝试其他所有选项后才使用。
使用 only()
并省略使用 select_related()
请求的字段也是错误的。另一方面,在不带任何参数的情况下调用 only()
将返回查询集获取的每个字段(包括注释)。
与 defer()
一样,无法从异步代码访问未加载的字段并期望它们加载。相反,您将收到 SynchronousOnlyOperation
异常。确保在 only()
调用中包含所有可能访问的字段。
using()
¶
- using(alias)¶
如果您使用多个数据库,则此方法用于控制 QuerySet
将针对哪个数据库进行评估。此方法仅接受一个参数,即数据库的别名,如 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=(), no_key=False)¶
返回一个查询集,该查询集将锁定行直到事务结束,在支持的数据库上生成 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)
来忽略锁定的行。nowait
和 skip_locked
是互斥的,尝试使用这两个选项启用 select_for_update()
将导致 ValueError
。
默认情况下,select_for_update()
锁定查询选择的全部行。例如,除了查询集模型的行之外,select_related()
中指定的相关对象的行也被锁定。如果不需要这样做,请在 select_for_update(of=(...))
中指定要锁定的相关对象,使用与 select_related()
相同的字段语法。使用值 'self'
来引用查询集的模型。
在 select_for_update(of=(...))
中锁定父模型
如果您在使用多表继承时想要锁定父模型,则必须在 of
参数中指定父链接字段(默认为 <parent_model_name>_ptr
)。例如
Restaurant.objects.select_for_update(of=("self", "place_ptr"))
使用带指定字段的 select_for_update(of=(...))
如果您想要锁定模型并指定选定的字段,例如使用 values()
,则必须从 of
参数中的每个模型中至少选择一个字段。没有选择字段的模型将不会被锁定。
仅在 PostgreSQL 上,您可以传递 no_key=True
以获取一个较弱的锁,该锁在锁生效时仍然允许创建仅引用锁定行的行(例如,通过外键)。PostgreSQL 文档中有关于行级锁模式的更多详细信息。
您不能在可为空的关系上使用 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: ...)>, ...]>
postgresql
、oracle
和 mysql
数据库后端支持 select_for_update()
。但是,MariaDB 仅支持 nowait
参数,MariaDB 10.6+ 也支持 skip_locked
参数,MySQL 支持 nowait
、skip_locked
和 of
参数。no_key
参数仅在 PostgreSQL 上受支持。
使用不支持这些选项的数据库后端(例如 MySQL)将 nowait=True
、skip_locked=True
、no_key=True
或 of
传递给 select_for_update()
将引发 NotSupportedError
。这可以防止代码意外阻塞。
在自动提交模式下评估使用 select_for_update()
的查询集,在支持 SELECT ... FOR UPDATE
的后端上是一个 TransactionManagementError
错误,因为在这种情况下行不会被锁定。如果允许,这将导致数据损坏,并且很容易由在事务之外期望在事务中运行的调用代码引起。
在不支持 SELECT ... FOR UPDATE
的后端(如 SQLite)上使用 select_for_update()
将不起作用。SELECT ... FOR UPDATE
不会添加到查询中,并且如果在自动提交模式下使用 select_for_update()
,则不会引发错误。
警告
尽管 select_for_update()
通常在自动提交模式下失败,但由于 TestCase
会自动将每个测试包装在一个事务中,因此即使在 atomic()
块之外在 TestCase
中调用 select_for_update()
也会(可能出乎意料地)通过而不会引发 TransactionManagementError
。要正确测试 select_for_update()
,您应该使用 TransactionTestCase
。
某些表达式可能不受支持
PostgreSQL 不支持带有 Window
表达式的 select_for_update()
。
raw()
¶
- raw(raw_query, params=(), translations=None, using=None)¶
获取一个原始 SQL 查询,执行它,并返回一个 django.db.models.query.RawQuerySet
实例。此 RawQuerySet
实例可以像普通 QuerySet
一样进行迭代以提供对象实例。
请参阅执行原始 SQL 查询以获取更多信息。
警告
raw()
始终触发一个新的查询,并且不会考虑之前的过滤条件。因此,它通常应该从 Manager
或一个新的 QuerySet
实例中调用。
返回新 QuerySet
的运算符¶
组合的 QuerySet 必须使用相同的模型。
AND (&
)¶
使用 SQL AND
运算符组合两个 QuerySet
,其方式类似于链接过滤器。
以下等效
Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1).filter(y=2)
SQL 等效
SELECT ... WHERE x=1 AND y=2
OR (|
)¶
使用 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
|
不是一个交换运算,因为可能会生成不同的(但等效的)查询。
XOR (^
)¶
使用 SQL XOR
运算符组合两个 QuerySet
。 XOR
表达式匹配由奇数个操作数匹配的行。
以下等效
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 XOR y=2
注意
XOR
在 MariaDB 和 MySQL 中得到原生支持。在其他数据库上,x ^ y ^ ... ^ z
将转换为等效的
(x OR y OR ... OR z) AND
1=MOD(
(CASE WHEN x THEN 1 ELSE 0 END) +
(CASE WHEN y THEN 1 ELSE 0 END) +
...
(CASE WHEN z THEN 1 ELSE 0 END),
2
)
在旧版本中,对于不支持 SQL XOR
运算符的数据库,XOR
返回由正好一个操作数匹配的行。之前的行为与 MySQL、MariaDB 和 Python 行为不一致。
不返回 QuerySet
的方法¶
以下 QuerySet
方法评估 QuerySet
并返回除了 QuerySet
之外的其他内容。
这些方法不使用缓存(请参阅缓存和 QuerySet)。相反,它们在每次被调用时都会查询数据库。
由于这些方法会评估 QuerySet,因此它们是阻塞调用,因此它们的同步版本不能从异步代码中调用。出于这个原因,每个方法都有一个对应的异步版本,前缀为 a
- 例如,而不是 get(…)
,你可以使用 await aget(…)
。
除了它们的异步特性外,通常行为没有区别,但任何差异都将在下面每个方法旁边注明。
get()
¶
- get(*args, **kwargs)¶
- aget(*args, **kwargs)¶
异步版本:aget()
返回与给定查找参数匹配的对象,这些参数应采用字段查找中描述的格式。你应该使用保证唯一的查找,例如主键或唯一约束中的字段。例如
Entry.objects.get(id=1)
Entry.objects.get(Q(blog=blog) & Q(entry_number=1))
如果你期望 QuerySet 已经返回一行,则可以使用不带任何参数的 get()
返回该行的对象
Entry.objects.filter(pk=1).get()
如果 get()
未找到任何对象,则会引发 Model.DoesNotExist
异常
Entry.objects.get(id=-999) # raises Entry.DoesNotExist
如果 get()
找到多个对象,则会引发 Model.MultipleObjectsReturned
异常
Entry.objects.get(name="A Duplicated Name") # raises Entry.MultipleObjectsReturned
这两个异常类都是模型类的属性,并且特定于该模型。如果你想处理来自不同模型的多个 get()
调用的此类异常,则可以使用它们的通用基类。例如,你可以使用 django.core.exceptions.ObjectDoesNotExist
处理来自多个模型的 DoesNotExist
异常
from django.core.exceptions import ObjectDoesNotExist
try:
blog = Blog.objects.get(id=1)
entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
print("Either the blog or entry doesn't exist.")
create()
¶
- create(**kwargs)¶
- acreate(**kwargs)¶
异步版本:acreate()
一种创建对象并一步保存它的便捷方法。因此
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)¶
- aget_or_create(defaults=None, **kwargs)¶
异步版本:aget_or_create()
一种查找具有给定 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
。
警告
假设数据库强制执行关键字参数的唯一性,此方法是原子的(请参阅unique
或unique_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"})
当您使用手动指定的 primary key 时,get_or_create()
方法具有与 create()
类似的错误行为。如果需要创建对象且键已存在于数据库中,则会引发 IntegrityError
。
最后,关于在 Django 视图中使用 get_or_create()
的说明。请确保仅在 POST
请求中使用它,除非您有充分的理由不这样做。 GET
请求不应对数据有任何影响。相反,在请求到页面的操作会对您的数据产生副作用时,请使用 POST
。更多信息,请参阅 HTTP 规范中的 安全方法。
警告
您可以通过 ManyToManyField
属性和反向关系使用 get_or_create()
。在这种情况下,您将限制该关系上下文中查询。如果您不一致地使用它,可能会导致一些完整性问题。
以下为模型示例
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 的 chapters 字段使用 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, create_defaults=None, **kwargs)¶
- aupdate_or_create(defaults=None, create_defaults=None, **kwargs)¶
异步版本: aupdate_or_create()
一个方便的方法,用于使用给定的 kwargs
更新对象,如果必要则创建一个新对象。 create_defaults
和 defaults
都是 (field, value) 对的字典。 create_defaults
和 defaults
中的值都可以是可调用对象。 defaults
用于更新对象,而 create_defaults
用于创建操作。如果未提供 create_defaults
,则 defaults
将用于创建操作。
返回一个 (object, created)
元组,其中 object
是创建或更新的对象,created
是一个布尔值,指定是否创建了一个新对象。
update_or_create
方法尝试根据给定的 kwargs
从数据库中获取对象。如果找到匹配项,则更新 defaults
字典中传递的字段。
这旨在作为样板代码的快捷方式。例如
defaults = {"first_name": "Bob"}
create_defaults = {"first_name": "Bob", "birthday": date(1940, 10, 9)}
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(create_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"},
create_defaults={"first_name": "Bob", "birthday": date(1940, 10, 9)},
)
有关如何解析 kwargs
中传递的名称的详细说明,请参阅 get_or_create()
。
如上文 get_or_create()
中所述,此方法容易出现竞争条件,如果未在数据库级别强制执行唯一性,则可能导致同时插入多行。
与 get_or_create()
和 create()
一样,如果您使用手动指定的 primary key 并且需要创建对象但键已存在于数据库中,则会引发 IntegrityError
。
添加了 create_defaults
参数。
bulk_create()
¶
- bulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)¶
- abulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)¶
异步版本: abulk_create()
此方法以高效的方式将提供的对象列表插入数据库(通常只有一个查询,无论有多少个对象),并按提供的顺序返回创建的对象列表。
>>> objs = Entry.objects.bulk_create(
... [
... Entry(headline="This is a test"),
... Entry(headline="This is only a test"),
... ]
... )
但它有一些注意事项
不会调用模型的
save()
方法,也不会发送pre_save
和post_save
信号。它不适用于多表继承场景中的子模型。
如果模型的 primary key 是
AutoField
并且ignore_conflicts
为 False,则 primary key 属性只能在某些数据库上检索(目前为 PostgreSQL、MariaDB 和 SQLite 3.35+)。在其他数据库上,它不会被设置。它不适用于多对多关系。
它将
objs
转换为列表,如果objs
是生成器,则会完全评估objs
。该转换允许检查所有对象,以便可以先插入任何具有手动设置的 primary key 的对象。如果您希望分批插入对象而不一次性评估整个生成器,则可以使用此技术,只要对象没有任何手动设置的 primary key 即可。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,SQLite 的默认值为每个查询最多使用 999 个变量。
在支持它的数据库(除了 Oracle)上,将 ignore_conflicts
参数设置为 True
会告诉数据库忽略插入任何因违反约束(如重复唯一值)而失败的行。
在支持该功能的数据库(除 Oracle 外的所有数据库)中,将 update_conflicts
参数设置为 True
会告诉数据库在行插入冲突失败时更新 update_fields
。在 PostgreSQL 和 SQLite 中,除了 update_fields
之外,还必须提供可能发生冲突的 unique_fields
列表。
启用 ignore_conflicts
参数会禁用为每个模型实例设置主键(如果数据库通常支持该功能)。
在旧版本中,启用 update_conflicts
参数会阻止为每个模型实例设置主键。
警告
在 MySQL 和 MariaDB 中,将 ignore_conflicts
参数设置为 True
会将某些类型的错误(重复键错误除外)转换为警告,即使在严格模式下也是如此。例如:无效值或非空违规。有关更多详细信息,请参阅 MySQL 文档 和 MariaDB 文档。
bulk_update()
¶
- bulk_update(objs, fields, batch_size=None)¶
- abulk_update(objs, fields, batch_size=None)¶
异步版本:abulk_update()
此方法有效地更新提供的模型实例上的指定字段,通常使用一个查询,并返回更新的对象数量。
>>> 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"])
2
使用 QuerySet.update()
保存更改,因此这比遍历模型列表并在每个模型上调用 save()
更有效率,但它有一些注意事项。
无法更新模型的主键。
如果在大量行中更新大量列,生成的 SQL 可能非常大。通过指定合适的
batch_size
来避免这种情况。更新在多表继承祖先上定义的字段将为每个祖先产生一个额外的查询。
当单个批次包含重复项时,只有该批次中的第一个实例才会导致更新。
函数返回的已更新对象数量可能少于传入的对象数量。这可能是由于传入的重复对象在同一批次中更新,或者由于竞争条件导致对象不再存在于数据库中。
batch_size
参数控制在单个查询中保存多少个对象。默认情况下,所有对象都将在一个批次中更新,但 SQLite 和 Oracle 对查询中使用的变量数量有限制。
count()
¶
- count()¶
- acount()¶
异步版本:acount()
返回一个整数,表示与 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)
可能更有效,它不会像 count()
那样导致额外的数据库查询。
如果查询集已完全检索,count()
将使用该长度而不是执行额外的数据库查询。
in_bulk()
¶
- in_bulk(id_list=None, *, field_name='pk')¶
- ain_bulk(id_list=None, *, field_name='pk')¶
异步版本:ain_bulk()
获取字段值列表 (id_list
) 和这些值的 field_name
,并返回一个字典,将每个值映射到具有给定字段值的 object 实例。 in_bulk
永远不会引发 django.core.exceptions.ObjectDoesNotExist
异常;也就是说,任何与任何实例都不匹配的 id_list
值都将被忽略。如果未提供 id_list
,则返回查询集中的所有对象。field_name
必须是唯一的字段或一个不同的字段(如果 distinct()
中只指定了一个字段)。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>}
>>> Blog.objects.distinct("name").in_bulk(field_name="name")
{'Beatles Blog': <Blog: Beatles Blog>, 'Cheddar Talk': <Blog: Cheddar Talk>, 'Django Weblog': <Blog: Django Weblog>}
如果将空列表传递给 in_bulk()
,则会得到一个空字典。
iterator()
¶
- iterator(chunk_size=None)¶
- aiterator(chunk_size=None)¶
异步版本:aiterator()
评估 QuerySet
(通过执行查询)并返回结果的迭代器(参见 PEP 234),或者如果您调用其异步版本 aiterator
,则返回异步迭代器(参见 PEP 492)。
QuerySet
通常会在内部缓存其结果,以便重复评估不会导致额外的查询。相反,iterator()
将直接读取结果,而不会在 QuerySet
级别进行任何缓存(在内部,默认迭代器调用 iterator()
并缓存返回值)。对于返回大量对象且您只需要访问一次的 QuerySet
,这可以提高性能并显着减少内存使用。
请注意,在已评估的 QuerySet
上使用 iterator()
将强制它再次评估,重复查询。
iterator()
与之前对 prefetch_related()
的调用兼容,只要提供了 chunk_size
参数。较大的值需要较少的查询来完成预取,但会消耗更多的内存。
添加了对 aiterator()
与之前对 prefetch_related()
的调用兼容性。
在某些数据库(例如 Oracle、SQLite)中,SQL IN
子句中项的最大数量可能受到限制。因此,应使用低于此限制的值。(特别是,当跨两个或多个关系进行预取时,chunk_size
应该足够小,以确保每个预取关系的预期结果数量仍然低于限制。)
只要 QuerySet 没有预取任何相关对象,不为 chunk_size
提供值将导致 Django 使用隐式默认值 2000。
根据数据库后端,查询结果将一次性加载或使用服务器端游标从数据库流式传输。
使用服务器端游标¶
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 从数据库驱动程序检索的批次大小。较大的批次减少了与数据库驱动程序通信的开销,但会略微增加内存消耗。
只要 QuerySet 没有预取任何相关对象,不为 chunk_size
提供值将导致 Django 使用隐式默认值 2000,该值源自 psycopg 邮件列表上的计算
假设包含 10-20 列混合文本和数字数据的行,2000 将获取少于 100KB 的数据,这似乎是在传输的行数和如果循环过早退出而丢弃的数据之间的一个很好的折衷方案。
latest()
¶
- latest(*fields)¶
- alatest(*fields)¶
异步版本:alatest()
根据给定的字段返回表中的最新对象。
此示例根据 pub_date
字段返回表中的最新 Entry
。
Entry.objects.latest("pub_date")
您还可以根据多个字段选择最新对象。例如,当两个条目具有相同的 pub_date
时,选择 expire_date
最早的 Entry
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)¶
- aearliest(*fields)¶
异步版本:aearliest()
除了方向更改外,其他方面的工作原理与 latest()
相同。
first()
¶
- first()¶
- afirst()¶
异步版本:afirst()
返回查询集中匹配的第一个对象,如果不存在匹配的对象,则返回 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()¶
- alast()¶
异步版本:alast()
工作原理与 first()
相同,但返回查询集中的最后一个对象。
aggregate()
¶
- aggregate(*args, **kwargs)¶
- aaggregate(*args, **kwargs)¶
异步版本:aaggregate()
返回一个包含聚合值(平均值、总和等)的字典,这些值是在QuerySet
上计算的。传递给aggregate()
的每个参数指定一个将包含在返回字典中的值。
Django 提供的聚合函数在下面的聚合函数中进行了描述。由于聚合也是查询表达式,因此可以将聚合与其他聚合或值组合以创建复杂的聚合。
使用关键字参数指定的聚合将使用该关键字作为注释的名称。匿名参数将根据聚合函数的名称和正在聚合的模型字段生成一个名称。复杂聚合不能使用匿名参数,并且必须指定关键字参数作为别名。
例如,当处理博客条目时,可能需要知道有多少作者贡献了博客条目。
>>> from django.db.models import Count
>>> Blog.objects.aggregate(Count("entry"))
{'entry__count': 16}
通过使用关键字参数指定聚合函数,可以控制返回的聚合值的名称。
>>> Blog.objects.aggregate(number_of_entries=Count("entry"))
{'number_of_entries': 16}
有关聚合的深入讨论,请参阅聚合主题指南。
exists()
¶
- exists()¶
- aexists()¶
异步版本:aexists()
如果QuerySet
包含任何结果,则返回True
,否则返回False
。这尝试以最简单和最快的方式执行查询,但它确实执行与普通QuerySet
查询几乎相同的查询。
exists()
用于搜索与QuerySet
中是否存在任何对象相关的信息,特别是在大型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)
做更多的整体工作(一个用于存在性检查的查询加上一个用于稍后检索结果的额外查询),后者检索结果然后检查是否返回了任何结果。
contains()
¶
- contains(obj)¶
- acontains(obj)¶
异步版本:acontains()
如果QuerySet
包含obj
,则返回True
,否则返回False
。这尝试以最简单和最快的方式执行查询。
contains()
用于检查对象是否属于QuerySet
,特别是在大型QuerySet
的上下文中。
要检查查询集是否包含特定项目
if some_queryset.contains(obj):
print("Entry contained in queryset")
这将比以下方法更快,后者需要评估并遍历整个查询集
if obj in some_queryset:
print("Entry contained in queryset")
与exists()
类似,如果some_queryset
尚未被评估,但知道它将在某个时间点被评估,那么使用some_queryset.contains(obj)
将执行额外的数据库查询,通常会导致整体性能下降。
update()
¶
- update(**kwargs)¶
- aupdate(**kwargs)¶
异步版本:aupdate()
对指定的字段执行 SQL 更新查询,并返回匹配的行数(如果某些行已经具有新值,则此数可能不等于更新的行数)。
例如,要关闭 2010 年发布的所有博客条目的评论,可以执行以下操作
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(假设Entry
模型具有pub_date
和comments_on
字段。)
可以更新多个字段——没有限制。例如,这里更新了comments_on
和headline
字段。
>>> 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
或post_save
信号(这是调用Model.save()
的结果)。如果要更新具有自定义save()
方法的模型的一批记录,请循环遍历它们并调用save()
,如下所示
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
有序查询集¶
仅在 MariaDB 和 MySQL 上支持将order_by()
与update()
链接,对于不同的数据库,将忽略此操作。这对于按指定顺序更新唯一字段以避免冲突非常有用。例如
Entry.objects.order_by("-number").update(number=F("number") + 1)
注意
如果order_by()
子句包含注释、继承的字段或跨越关系的查找,则将被忽略。
delete()
¶
- delete()¶
- adelete()¶
异步版本:adelete()
对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, {'blog.Entry': 2, 'blog.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, {'blog.Blog': 1, 'blog.Entry': 2, 'blog.Entry_authors': 2})
这种级联行为可以通过 on_delete
参数(在 ForeignKey
中)进行自定义。
delete()
方法执行批量删除,不会在你的模型上调用任何 delete()
方法。但是,它会为所有被删除的对象(包括级联删除的对象)发出 pre_delete
和 post_delete
信号。
Django 需要将对象提取到内存中才能发送信号并处理级联。但是,如果没有级联和信号,那么 Django 可能会走快速路径,并在不提取到内存的情况下删除对象。对于大型删除操作,这可以显著减少内存使用量。执行的查询数量也可以减少。
设置为 on_delete
DO_NOTHING
的 ForeignKeys 不会阻止删除操作走快速路径。
请注意,对象删除中生成的查询是实现细节,可能随时更改。
as_manager()
¶
- classmethod as_manager()¶
类方法,返回一个 Manager
实例,其中包含 QuerySet
方法的副本。有关更多详细信息,请参阅 使用 QuerySet 方法创建管理器。
请注意,与本节中的其他条目不同,此条目没有异步变体,因为它不执行查询。
explain()
¶
- explain(format=None, **options)¶
- aexplain(format=None, **options)¶
异步版本:aexplain()
返回 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)
不同数据库之间的输出差异很大。
除了 Oracle 之外,所有内置数据库后端都支持 explain()
,因为在 Oracle 中实现它并不简单。
format
参数会更改数据库默认输出格式,该格式通常是基于文本的。PostgreSQL 支持 'TEXT'
、'JSON'
、'YAML'
和 'XML'
格式。MariaDB 和 MySQL 支持 'TEXT'
(也称为 'TRADITIONAL'
)和 'JSON'
格式。MySQL 8.0.16+ 还支持改进的 'TREE'
格式,它类似于 PostgreSQL 的 'TEXT'
输出,如果支持,则默认为此格式。
一些数据库接受可以返回有关查询的更多信息的标志。将这些标志作为关键字参数传递。例如,在使用 PostgreSQL 时
>>> print(Blog.objects.filter(title="My Blog").explain(verbose=True, analyze=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
在某些数据库上,标志可能会导致查询执行,这可能会对你的数据库产生不利影响。例如,MariaDB、MySQL 8.0.18+ 和 PostgreSQL 支持的 ANALYZE
标志,如果存在触发器或调用函数,即使对于 SELECT
查询,也可能导致数据更改。
添加了对 PostgreSQL 16+ 上 generic_plan
选项的支持。
Field
查找¶
字段查找是指定 SQL WHERE
子句主体的方式。它们作为关键字参数指定给 QuerySet
方法 filter()
、exclude()
和 get()
。
有关简介,请参阅 模型和数据库查询文档。
Django 的内置查找如下所示。还可以为模型字段编写 自定义查找。
为了方便起见,当没有提供查找类型时(如在 Entry.objects.get(id=14)
中),将假定查找类型为 exact
。
exact
¶
精确匹配。如果提供的比较值是 None
,则将其解释为 SQL NULL
(有关更多详细信息,请参阅 isnull
)。
示例
Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)
SQL 等价物
SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;
iexact
¶
不区分大小写的精确匹配。如果提供的比较值是 None
,则将其解释为 SQL NULL
(有关更多详细信息,请参阅 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))
请注意围绕 Blog QuerySet
的 list()
调用强制执行第一个查询。如果没有它,将执行嵌套查询,因为 QuerySet 是惰性的。
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 代码片段,因为相关查询的实现因不同的数据库引擎而异。)
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 语法都不同。)
iso_year
¶
对于日期和日期时间字段,精确的 ISO 8601 周编号年份匹配。允许链接其他字段查找。接受整数年份。
示例
Entry.objects.filter(pub_date__iso_year=2005)
Entry.objects.filter(pub_date__iso_year__gte=2005)
(每个数据库引擎的精确 SQL 语法都不同。)
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 语法都不同。)
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 语法都不同。)
注意这将匹配任何在当月第 3 天的 pub_date 的记录,例如 1 月 3 日、7 月 3 日等。
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 代码片段,因为相关查询的实现因不同的数据库引擎而异。)
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 为星期六。
iso_week_day
¶
对于日期和日期时间字段,精确的 ISO 8601 一周中的某一天匹配。允许链接其他字段查找。
接受表示一周中某一天的整数,从 1(星期一)到 7(星期日)。
示例
Entry.objects.filter(pub_date__iso_week_day=1)
Entry.objects.filter(pub_date__iso_week_day__gte=1)
(此查找不包含等效的 SQL 代码片段,因为相关查询的实现因不同的数据库引擎而异。)
注意这将匹配任何 pub_date 恰逢星期一(一周中的第 1 天)的记录,无论发生在哪个月或哪一年。一周中的天数索引为 1 为星期一,7 为星期日。
quarter
¶
对于日期和日期时间字段,“一年中的季度”匹配。允许链接其他字段查找。接受 1 到 4 之间的整数,表示一年中的季度。
检索第二季度(4 月 1 日至 6 月 30 日)的条目的示例
Entry.objects.filter(pub_date__quarter=2)
(此查找不包含等效的 SQL 代码片段,因为相关查询的实现因不同的数据库引擎而异。)
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 代码片段,因为相关查询的实现因不同的数据库引擎而异。)
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 语法都不同。)
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 语法都不同。)
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 语法都不同。)
isnull
¶
接受 True
或 False
,分别对应于 SQL 查询 IS NULL
和 IS NOT NULL
。
示例
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 中对日期/时间字段使用聚合将引发 NotSupportedError
错误。
空查询集或组
当与空 QuerySet
或组一起使用时,聚合函数将返回 None
。例如,如果 QuerySet
不包含任何条目或非空 QuerySet
中的任何组为空,则 Sum
聚合函数将返回 None
而不是 0
。要改为返回其他值,请定义 default
参数。 Count
是此行为的例外;如果 QuerySet
为空,则它将返回 0
,因为 Count
不支持 default
参数。
所有聚合函数都具有以下共同参数
expressions
¶
引用模型上字段的字符串,字段的转换或查询表达式。
output_field
¶
一个可选参数,表示返回值的模型字段
注意
当组合多个字段类型时,只有当所有字段类型都相同,Django 才能确定 output_field
。否则,您必须自己提供 output_field
。
filter
¶
一个可选的Q 对象
,用于筛选要聚合的行。
default
¶
一个可选参数,允许指定一个值作为查询集(或分组)不包含任何条目时的默认值。
**extra
¶
关键字参数,可以为聚合生成的 SQL 提供额外的上下文。
Avg
¶
- class Avg(expression, output_field=None, distinct=False, filter=None, default=None, **extra)[source]¶
返回给定表达式的平均值,除非您指定了不同的
output_field
,否则该表达式必须是数字类型。默认别名:
<field>__avg
返回类型:如果输入为
int
,则返回float
,否则与输入字段相同,或者如果提供了output_field
,则返回output_field
。如果查询集或分组为空,则返回default
。
- distinct¶
可选。如果
distinct=True
,则Avg
返回唯一值的平均值。这相当于 SQL 中的AVG(DISTINCT <field>)
表达式。默认值为False
。
Count
¶
Max
¶
Min
¶
StdDev
¶
Sum
¶
Variance
¶
- class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra)[source]¶
返回提供表达式中数据的方差。
默认别名:
<field>__variance
返回类型:如果输入为
int
,则返回float
,否则与输入字段相同,或者如果提供了output_field
,则返回output_field
。如果查询集或分组为空,则返回default
。
- sample¶
可选。默认情况下,
Variance
返回总体方差。但是,如果sample=True
,则返回值将为样本方差。