Django4.0 数据库访问优化-不要检索你不需要的东西
使用 QuerySet.values() 和 values_list()
当你只想得到字典或列表的值,并且不需要 ORM
模型对象时,可以适当使用 values()
。这些对于替换模板代码中的模型对象非常有用——只要你提供的字典具有与模板中使用时相同的属性就行。
使用 QuerySet.defer() 和 only()
如果你明确不需要这个数据库列(或在大部分情况里不需要),使用 defer()
和 only()
来避免加载它们。注意如果你使用它们,ORM
将必须在单独的查询中获取它们,如果你不恰当的使用,会让事情变得糟糕。
不要在没有分析的情况下过分使用延迟字段,因为数据库必须从磁盘中读取结果中单行的大部分非文本、非VARCHAR数据,即使它最终只使用的几列。当你不想加载许多文本数据或需要大量处理来转换回 Python的字段, defer()
和 only()
方法最有用。总之,先分析,再优化。
使用 QuerySet.contains(obj)
如果您只想找出 obj 是否在查询集中,而不是 obj 在查询集中。
使用 QuerySet.count()
如果你只想计数,不要使用 len(queryset)
。
使用 QuerySet.exists()
若你只想要确认是否有至少存在一项满足条件的结果,而不是 if queryset
。
不要过度使用 contains()、count() 和 exists()
如果你需要查询集中的其他数据,请立即对其进行评估。
假设 Group 模型与 User 具有多对多关系,则以下代码是最佳的:
members = group.members.all()
if display_group_members:
if members:
if current_user in members:
print("You and", len(members) - 1, "other users are members of this group.")
else:
print("There are", len(members), "members in this group.")
for member in members:
print(member.username)
else:
print("There are no members in this group.")
这是最佳的,因为:
- 由于
QuerySet
是惰性的,因此如果 display_group_members
为 False
,则不会进行数据库查询。 - 将
group.members.all()
存储在 members
变量中可以重用其结果缓存。 -
if members
: 导致 QuerySet.__bool__()
被调用,这导致 group.members.all()
查询在数据库上运行。 如果没有任何结果,它将返回 False
,否则返回 True
。 -
if current_user in members
:检查用户是否在结果缓存中,因此不会发出额外的数据库查询。 -
len(members)
的使用调用了 QuerySet.__len__()
,重用了结果缓存,因此再次没有发出数据库查询。 -
for member
循环遍历结果缓存。
使用 QuerySet.update() 和 delete()
如果要设置一些值并单独保存它们,而不是检索对象,那么可以通过 QuerySet.update()
使用批量 SQL UPDATE
语句。类似地,尽可能使用批量删除( bulk deletes
)。
注意,尽管这些批量更新方法不会调用单独实例的 save()
或 delete()
方法,这意味着你为这些方法添加的任何自定义行为都不会执行,包括来自正常数据库对象信号( signals
)的任何内容。
直接使用外键值
如果只需要外键值,那么使用已有对象上的外键值,而不是获取所有相关对象并获取它的主键。比如:
entry.blog_id
替换成:
entry.blog.id
如无需要,不要排序结果
排序是耗时的;对每个字段的排序是数据库必须执行的操作。如果模型有一个默认排序( Meta.ordering
)并且不需要它,那么可以通过调用没有参数的 order_by()
在查询集上删除它。
添加索引到你的数据库上可以帮助改进排序性能。