我正在尝试优化一些Django代码,并且我有两种类似的方法正在执行差异。以下是一些示例模型:
class A(models.Model):
name = models.CharField(max_length=100)
class B(models.Model):
name = models.CharField(max_length=100)
a = models.ForeignKey(A)
c = models.ForeignKey(C)
class C(models.Model):
name = models.CharField(max_length=100)
对于每个A
对象,我想迭代其传入的B
的子集,并根据其c
值进行过滤。简单:
for a in A.objects.all() :
for b in a.B_set.filter( c__name='some_val' ) :
print a.name, b.name, c.name
问题在于迭代的每个a
值都有一个新的数据库查找。
似乎解决方案是预取将输入过滤器的c值。
qs_A = A.objects.all().prefetch_related('B_set__c')
现在考虑以下两种过滤方法:
# Django filter
for a in qs_A :
for b in a.B_set.filter( c__name='some_val' ) :
print a.name, b.name, n.name
# Python filter
for a in qs_A :
for b in filter( lambda b: b.c.name == 'some_val', a.B_set.all() ):
print a.name, b.name, c.name
使用我正在使用的数据,django过滤器比python过滤器多出48个SQL查询(在12个元素qs_A
结果集上)。这让我相信django过滤器不会使用预取表。
有人可以解释发生了什么吗?
也许可以在预取期间应用过滤器?
答案 0 :(得分:1)
预取和过滤没有任何直接连接......过滤总是发生在数据库中,而prefetch_related
的主要目的是在输出相关对象时获取相关对象的数据。
较小的SQL查询通常更好,但如果您想优化您的用例,您应该执行一些基准测试和分析,而不是依赖于一些一般性语句!
如果您不首先使用A
,而是使用B
,则可以提高您的示例效率:
qs = B.objects.select_related('a', 'c').filter(c__name='some val')
# maybe you need some filtering for a as well:
# qs = qs.filter(a__x=....)
for b in qs:
print b.a.name, b.name, b.c.name
也许你需要在过滤后(在python中)进行一些重组/排序,但是如果你已经可以在一步中执行所有过滤操作,那么效率会更高...... 否则可能看看原始的SQL查询...