2011-08-19 53 views
1

這是一個從Django的官方文檔:https://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationshipsdjango:在多對多的關係中,多餘的領域是否有黑魔法?

的模型是這樣的:

class Person(models.Model): 
    name = models.CharField(max_length=128) 

    def __unicode__(self): 
     return self.name 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(Person, through='Membership') 

    def __unicode__(self): 
     return self.name 

class Membership(models.Model): 
    person = models.ForeignKey(Person) 
    group = models.ForeignKey(Group) 
    date_joined = models.DateField() 
    invite_reason = models.CharField(max_length=64) 

最後,它提供了這樣一個例子:

# Find all the members of the Beatles that joined after 1 Jan 1961 
>>> Person.objects.filter(
...  group__name='The Beatles', 
...  membership__date_joined__gt=date(1961,1,1)) 
[<Person: Ringo Starr] 

我的問題是人的模型是如何知道因爲它們沒有被定義在一個組和成員屬性中。它只是通過許多方面的魔法,或者它是Django中的一種通用方法?

如果我要實現相同的查詢,我想下面的代碼更自然(從Django的ORM的角度,而不是從企業之一):

Membership.objects.filter(group__name='The Beatles', date_joined__gt=date(1961,1,1))).select_related('person') 

編輯 我再次讀取該文件,並做發現這種倒退是普遍的。在https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships中,它提到:

它也向後工作。要引用「反向」關係,只需使用模型的小寫名稱 。

我從來沒有用過這個向後查詢。所以我第一次看到它,它激怒了我的大腦。對不起,這個線程原來是愚蠢的。但我希望它能幫助那些跳過該段中非常細線的人。

回答

2

它不是ManyToMany領域的魔力;它是一個與外鍵(包括ManyToMany)關係的django ORM的通用組件。

例如,如果你有

class Musician(models.Model): 
    name = models.CharField(max_length=128) 
    band = models.ForeignKey("Band") 

class Band(models.Model): 
    name = models.CharField(max_length=128) 
    genre = models.CharField(max_length=50) 

你可以做一些像Musician.objects.filter(band__name='The Beatles')查詢可以用Django的調試工具欄或Django的外殼,可以看出:

from django.db import connection 
connection.queries 

,並會複雜的JOIN語句。由ORM構建的SQL查詢看起來像:

SELECT "appname_musician"."id", "appname_musician"."name", "appname_musician"."band_id" 
    FROM "appname_musician" 
    INNER JOIN "appname_band" ON ("appname_musician"."band_id" = "appname_band"."id") 
    WHERE "appname_band"."name" = "The Beatles"; 

編輯: 如果沒有Band.objects.filter(musician__name = 'Ringo Starr')的ORM將它翻譯成:

SELECT "appname_band"."id", "appname_band"."name", "appname_band"."genre" 
    FROM "appname_band" 
    INNER JOIN "appname_musician" ON ("appname_musician"."band_id" = "appname_band"."id") 
    WHERE "appname_musician"."name" = "Ringo Starr"; 

的ORM知道的關係,並能將您的ORM查詢轉換爲適當的SQL。樂隊沒有音樂家的對象並不重要;你可以給django一個清晰的請求: 我想要所有的樂隊對象,那裏有音樂家與樂隊的名字'Ringo Starr'相連。

我想你可能會想到對象封裝(例如,樂隊沒有音樂家;你怎麼能根據它進行過濾)?它不是黑魔法,因爲過濾請求是清晰明確的 - 並且作爲參數而不是Band對象的成員;但命令過濾。例如,即使Band中沒有name__icontains對象,也可以執行Band.objects(name__icontains = "The")。 ORM將您的請求轉換爲SQL,這很容易執行。


EDIT2: 您的最後一個例子:

class Musician(models.Model): 
    name = models.CharField(max_length=128) 
    initial_band = models.ForeignKey("Band", related_name = 'initial_band_members') 
    final_band = models.ForeignKey("Band", related_name = 'final_band_members') 

class Band(models.Model): 
    name = models.CharField(max_length=128) 
    genre = models.CharField(max_length=50) 

try: ## create some objects 
    cream,_ = Band.objects.get_or_create(name="Cream", genre="Classic Rock") 
    derek, _ = Band.objects.get_or_create(name="Derek and the Dominos", genre="Classic Rock") 
    beatles, _ = Band.objects.get_or_create(name="The Beatles", genre="Classic Rock") 
    wings, _ = Band.objects.get_or_create(name="Wings", genre="Classic Rock") 
    Musician.objects.get_or_create(name="Eric Clapton", initial_band=cream, final_band=derek) 
    Musician.objects.get_or_create(name="Paul McCartney", initial_band=beatles, final_band=wings) 
    Musician.objects.get_or_create(name="John Lennon", initial_band=beatles, final_band=beatles) 

except: 
    pass 

然後,像Band.objects.filter(musician__name='Paul McCartney')查詢不工作(FieldError),但查詢像 Band.objects.filter(initial_band_members__name='Paul McCartney')會給後面的甲殼蟲樂隊用下面的SQL中(使用sqlite作爲db;該命令依賴於數據庫後端):

SELECT "testing_band"."id", "testing_band"."name", "testing_band"."genre" 
    FROM "testing_band" 
    INNER JOIN "testing_musician" ON ("testing_band"."id" = "testing_musician"."final_band_id") 
    WHERE "testing_musician"."name" = Paul McCartney ' 
+0

我可以完全理解你的例子,因爲樂隊是在音樂家模型中定義的外鍵。但在我的問題中,Person模型沒有定義人物屬性。爲了回答我的問題,請告訴我如何做相反的事情,比如Band.objects.filter(musician__name ='Ringo Starr')。 –

+0

這是相同的基本機制。 django ORM將您的django查詢轉換爲SQL。我將用一個例子來編輯。 –

+0

感謝您的解釋。我再次閱讀文檔並意識到反向查詢是可能的。但它是一個單線的東西。所以我想我的眼睛剛剛跳過它。 –

1

也許看看https://docs.djangoproject.com/en/dev/topics/db/queries/爲您的答案。通常使用連接。

+0

本文c不回答我的問題。在我的問題中,Person模型不知道組或成員資格屬性。我知道django會將group_set和membership_set管理器添加到它。但是組和成員屬性?沒有任何Django文檔提到過這種可能性。 –