2011-08-19 50 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文档提到过这种可能性。 –