QuerySetAPI参考
– 译自官方文档+自己的理解 –
本文档描述了QuerySetAPI 的详细信息。它建立在 模型 和 数据库查询 指南中提供的材料之上,因此在阅读本文档之前,您可能希望阅读并理解这些文档。
在整个参考文献中,我们将使用数据库查询指南中提供的示例Weblog模型。
1.什么时候QuerySet求值
本质上,QuerySet可以构造,过滤,切片,并且通常可以在不实际访问数据库的情况下传递。在您对queryset求值之前,实际上不会发生任何数据库活动。
您可以通过以下方式对QuerySet求值 :
(1)迭代。一个QuerySet是可迭代的,并且在您第一次迭代它时执行其数据库查询。例如,这将打印数据库中所有书籍的名称:
for e in Book.objects.all():
print(e.name)
注意:如果您只想确定是否存在至少一个结果,请不要使用此选项。使用exists()
效率更高。
>>>Book.objects.exists()
True
(2)切片。如 限制查询集 中所述,可以使用Python的数组切片语法对QuerySet进行切片。切片未评估的 QuerySet通常会返回另一个未评估的值QuerySet,但如果使用切片语法的“step”参数,Django将执行数据库查询,并返回一个列表。QuerySet对已经过评估的切片也会返回一个列表。
还要注意,即使对未评估的QuerySet切片返回另一个未评估QuerySet,进一步修改它(例如,添加更多过滤器,或修改排序)是不允许的,因为这不能很好地转换为SQL,它也没有明确的含义。
(3)腌制/缓存。有关腌制QuerySets时所涉及的内容的详细信息,请参阅以下部分。对于本节而言,重要的是从数据库中读取结果。
(4)repr()
。在QuerySet上调用repr()方法进行求值,这是为了方便Python交互式解释器,因此您可以在交互式使用API时立即看到结果。
(5)len()
。在QuerySet上调用len()方法进行求值. 正如您所料,这会返回结果列表的长度。
注意:如果您只需要确定集合中的记录数(并且不需要实际对象),那么使用SQL处理数据库级别的计数方法SELECT COUNT(*)
会更有效。Django 正是出于这个原因提供了一种count()
方法。
(6)list()
。在QuerySet上调用list()方法进行求值。
例子:
def fun05_queryset_api():
print(Book.objects.exists())
print(repr(Book.objects.all()))
print(len(Book.objects.all()))
print(Book.objects.all().count())
print(list(Book.objects.all()))
# 输出结果
<QuerySet [<Book: <呐喊>>, <Book: <哈利波特>>, <Book: <灭亡>>, <Book: <中国文人集>>]>
4
4
[<Book: <呐喊>>, <Book: <哈利波特>>, <Book: <灭亡>>, <Book: <中国文人集>>]
(7)bool()
。测试QuerySet在布尔上下文中,如使用 bool(),or,and或if语句,将导致查询被执行。如果至少有一个结果,QuerySet则为 True,否则False。例如:
if Book.objects.filter(name__contains="呐"):
print("There is at least one Book with the name contains 呐")
注意:如果您只想确定是否存在至少一个结果(并且不需要实际对象),则使用exists()会更有效。
2. 腌制(pickling)QuerySets
如果腌制 (pickle) 一个QuerySet,这将强制所有结果在腌制之前加载到内存中。腌制通常用作缓存的前身,并且当重新加载缓存的查询集时,您希望结果已经存在并且可以使用(从数据库读取可能需要一些时间,从而无法实现缓存)。这意味着当你取消腌制(unpickle)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的一部分。但是,如此处所述,对属性的内容进行pickle和unpickle是安全的(并且完全支持)。
您无法在版本之间共享pickles数据
腌制 QuerySets仅对用于生成它们的Django版本有效。如果使用Django版本N生成pickle,则无法保证使用Django版本N + 1可以读取pickle。腌制数据不应该用作长期存档的策略。
由于pickle兼容性错误很难诊断,例如无提示损坏的对象,因此当您尝试在Django版本中取消查询不同于其被pickle的查询集时,会引发RuntimeWarning错误。
3. QuerySet API
这是一个正式的QuerySet声明:
class QuerySet(model = None,query = None,using = None)
源代码
通常,当您与一个QuerySet进行交互时,您将通过 过滤器链 来使用它 。为了使其工作,大多数 QuerySet方法返回新的查询集。本节稍后将详细介绍这些方法。
该QuerySet 类有可用于内省的两个公共属性:
(1)ordered
如果QuerySet是有序的返回True - 即在模型上有一个order_by()子句或默认排序;否则返回False。
(2)db
现在执行此查询时将使用的数据库。
注解
QuerySet存在一个query参数,以便专门的查询子类可以重建内部查询状态。参数的值是该查询状态的不透明表示,并且不是公共API的一部分。简单地说:如果你需要问,你不需要使用它。
print(Book.objects.all().ordered)
print(Book.objects.order_by('name').ordered)
print(Book.objects.all().db)
print(Book.objects.all().query)
# 输出结果
False
True
default
SELECT "filter_book"."id", "filter_book"."name", "filter_book"."price", "filter_book"."category", "filter_book"."publishs_id" FROM "filter_book"
3.1 返回新QuerySets的方法
Django提供了一系列QuerySet细化方法,可以修改由其返回QuerySet的结果类型或执行SQL查询的方式。
filter()
filter(** kwargs)
返回一个QuerySet,包含与给定查找参数匹配的所有对象。
查询参数(**kwargs)
应采用下面的字段查找中描述的格式 。多个参数通过AND在底层SQL语句中连接起来。
如果需要执行更复杂的查询(例如,带OR语句的查询),则可以使用Q 对象。
exclude()
exclude(** kwargs)
返回一个QuerySet,包含与给定查找参数不匹配的所有对象。
查询参数(**kwargs)应采用下面的字段查找中描述的格式 。多个参数通过AND在底层SQL语句中连接起来,整个事件都包含在一个NOT()中。
此示例排除所有价格小于30并且书名包含“呐喊”的书籍:
print(Book.objects.exclude(price__lt=30,name__contains="呐喊"))
# 输出结果
<QuerySet [<Book: <哈利波特>>, <Book: <灭亡>>, <Book: <中国文人集>>]>
在SQL术语中,转化为:
SELECT ...
WHERE
NOT ("filter_book"."name" LIKE '%呐喊%'
AND "filter_book"."price" < 30)
此示例排除所有价格小于30或者书名包含“呐喊”的书籍::
print(Book.objects.exclude(price__lt=30).exclude(name__contains="呐喊"))
# 输出结果
<QuerySet [<Book: <哈利波特>>, <Book: <中国文人集>>]>
在SQL术语中,评估为:
SELECT ...
FROM "filter_book"
WHERE (NOT ("filter_book"."price" < 30)
AND
NOT ("filter_book"."name" LIKE '%呐喊%' )
请注意,第二个示例更具限制性。
如果需要执行更复杂的查询(例如,带OR语句的查询),则可以使用Q 对象。
annotate()
annotate(* args,** kwargs)