2013-05-25 201 views
1

我想優化一些Django代碼,並且我有兩種類似的方法表現不同。下面是一些例子型號:Django過濾與預過濾對象的python過濾

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

預取和過濾沒有任何直接連接...過濾總是發生在您的數據庫中,而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查詢...