django过滤与使用预取对象的python过滤

时间:2013-05-25 22:07:40

标签: django optimization

我正在尝试优化一些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过滤器不会使用预取表。

有人可以解释发生了什么吗?

也许可以在预取期间应用过滤器?

1 个答案:

答案 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查询...