在Django QuerySets中使用列表推导代替for循环
我正在尝试优化一个速度比较慢的应用程序。为此,我开始尽可能把所有的for循环改成列表推导式。
现在,我正在处理一个需要遍历Django查询集字典的函数。旧代码使用for循环来遍历这个字典,运行得很好。但是我用列表推导式写的代码返回的是Django查询集,而不是我想要的模型对象。
这是我的代码:
def get_children(parent):
# The following works
children = []
for value in get_data_map(parent).itervalues():
children += list(value)
# This part doesn't work as intended.
booms = [value for value in get_data_map(parent).itervalues() if value]
import pdb
pdb.set_trace()
(Pdb) type(children[0])
<class 'site.myapp.models.Children'>
(Pdb) type(booms[0])
<class 'django.db.models.query.QuerySet'>
需要注意的是,get_data_map返回的是一个字典,这个字典的值是<class 'django.db.models.query.QuerySet'>
这部分代码是应用程序中最耗时的部分之一。如果我能把这部分改成列表推导式,应用程序的速度希望能提高两倍。
有没有什么办法可以加快这部分代码的速度呢?
3 个回答
我同意,确实很难准确了解发生了什么,但使用 Django 调试工具条可以大大简化调试 Django 性能问题的过程:
https://github.com/django-debug-toolbar/django-debug-toolbar
如果问题是因为数据库请求太多,可以在工具条上查看“SQL 查询”标签,它会非常清楚地显示出来。
你的解决方案不行,因为你创建的是一个查询集的列表。每个值都是一个查询集,但你并没有把它们连接起来。对比一下:
a = [1,2,3]
b = [x for x in a if x ]
你会期待b也是一个整数的列表,对吧?你得到了查询集的列表(这比父级列表要好),但你仍然需要把它们连接起来。你可以使用itertools.chain来做到这一点:
http://docs.python.org/library/itertools.html#itertools.chain
foos = itertools.chain(booms)
foos应该是你想要的结果。
你的问题不是在于使用 for
循环还是列表推导(其实用生成器会更好)。真正的问题是 你对数据库的查询次数太多了。
因为你想得到一个列表,所以应该尝试通过一次查询来获取。如果你能解释一下你通常的查询集里有什么,我们可以告诉你怎么最好地合并它们。也许可以在 Q 对象上使用 OR
合并,或者构建一个整数集合,然后把它传给 __in=
过滤器。