2009-10-29 45 views
5

優化查詢我有以下的模型結構:Django的ORM:涉及到許多一對多的關係

class Container(models.Model): 
    pass 

class Generic(models.Model): 
    name = models.CharacterField(unique=True) 
    cont = models.ManyToManyField(Container, null=True) 
    # It is possible to have a Generic object not associated with any container, 
    # thats why null=True 

class Specific1(Generic): 
    ... 

class Specific2(Generic): 
    ... 

... 

class SpecificN(Generic): 
    ... 

說,我需要檢索所有Specific型車型,具有與特定容器的關係。

對於這個問題的SQL或多或少是微不足道的,但這不是問題。不幸的是,我在處理ORM(尤其是Django的ORM)方面經驗不足,所以我可能會在這裏錯過一個模式。

當一個強力的方式進行, -

c = Container.objects.get(name='somename') # this gets me the container 
items = c.generic_set.all() 
# this gets me all Generic objects, that are related to the container 
# Now what? I need to get to the actual Specific objects, so I need to somehow 
# get the type of the underlying Specific object and get it 
for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

這導致一噸分貝命中(每個通用的記錄,這涉及到一個容器),所以這顯然是沒有辦法的辦法去做吧。現在,它可能,也許,可以通過獲取SpecificX直接做對象:

s = Specific1.objects.filter(cont__name='somename') 
# This gets me all Specific1 objects for the specified container 
... 
# do it for every Specific type 

這樣的數據庫將再次爲每個特定類型(可以接受的,我猜)被擊中。

我知道,.select_related()不適用於m2m關係,因此在這裏沒有多大幫助。

重申,最終結果必須是SpecificX對象的集合(非泛型)。

+0

在反思,這個問題似乎有點毫無意義的我,現在的我已經提供了唯一可能的答案。畢竟,沒有辦法從任意字段的許多表中獲得聯合結果集。好吧,顯然,這是一種方式,但它很醜,涉及動態sql和/或「select *」。我認爲。 – shylent 2009-10-29 12:39:47

+0

您的問題在這裏的確與優化ManyToMany關係無關,並且與優化多表繼承的查詢無關。這確實是一個難題。 – 2009-10-29 14:30:22

+0

現在我想起來了,讓我相信這個問題是關於M2M關係的,事實上,select_related並不會遍歷多對多的關係。 – shylent 2009-10-29 19:08:46

回答

2

我想你已經概述了兩種簡單的可能性。您可以對Generic執行單個過濾器查詢,然後將每個項目轉換爲其特定的子類型(結果爲n + 1個查詢,其中n是返回的項目數量),或者對每個特定的表格進行單獨的查詢(結果k查詢,其中k是特定類型的數量)。

它實際上是值得基準,看看其中哪些在現實中更快。第二種似乎更好,因爲它可能(更少)查詢,但每個查詢都必須使用m2m中間表執行連接。在前一種情況下,您只能執行一個連接查詢,然後執行許多簡單連接查詢。一些數據庫後端在大量小型查詢中執行得更好,而不是更少,更復雜。

如果第二個實際上對於您的用例來說確實快得多,並且您願意做一些額外的工作來清理代碼,那麼應該可以爲Generic模型編寫一個自定義管理器方法,從給定查詢集的相關特定表中提取所有子類型數據,每個子類型表只使用一個查詢;類似於this snippet通過批量預取來優化通用外鍵。這將爲您提供與第二個選項相同的查詢,DRYer語法是您的第一個選項。

+0

不知道誰降低了這一點。是的,我會研究編寫自定義管理器的可能性,但是,正如我所說的,我對ORM的經驗非常有限(儘管我沒有SQL問題),所以內部仍然有點給我一個黑匣子。無論如何,我會去看看我能做些什麼。 – shylent 2009-10-29 15:18:24

1

不是一個完整的答案,但你可以這樣做

items= list(items) 
for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

,而不是該回避命中了大量的:

for item in items: 
    spec = getattr(item, item.get_my_specific_type()) 

事實上,通過強制強制轉換爲一個Python列表,你強制django orm加載你的查詢集中的所有元素。然後它在一個查詢中執行此操作。

+0

這是一個很好的提示(在文檔中提到,我已閱讀:)。也不太明顯。 – shylent 2009-10-29 12:31:07

+3

嗯,這是不正確的。在這種情況下投射到列表中沒有任何區別。在這兩個版本中,只有一個查詢是針對通用表完成的,並且在兩個版本中,針對每個項目的SpecificX表執行一個查詢。兩者的查詢數量相同。 – 2009-10-29 14:28:28