在Django QuerySets中使用列表推导代替for循环

3 投票
3 回答
7550 浏览
提问于 2025-04-16 21:39

我正在尝试优化一个速度比较慢的应用程序。为此,我开始尽可能把所有的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 个回答

1

我同意,确实很难准确了解发生了什么,但使用 Django 调试工具条可以大大简化调试 Django 性能问题的过程:

https://github.com/django-debug-toolbar/django-debug-toolbar

如果问题是因为数据库请求太多,可以在工具条上查看“SQL 查询”标签,它会非常清楚地显示出来。

2

你的解决方案不行,因为你创建的是一个查询集的列表。每个值都是一个查询集,但你并没有把它们连接起来。对比一下:

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应该是你想要的结果。

5

你的问题不是在于使用 for 循环还是列表推导(其实用生成器会更好)。真正的问题是 你对数据库的查询次数太多了

因为你想得到一个列表,所以应该尝试通过一次查询来获取。如果你能解释一下你通常的查询集里有什么,我们可以告诉你怎么最好地合并它们。也许可以在 Q 对象上使用 OR 合并,或者构建一个整数集合,然后把它传给 __in= 过滤器。

撰写回答