来自Django的官方文档:https://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships
模型如下:
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]
我的问题是Person模型如何识别组和成员资格属性,因为它们没有在其上定义。是不是通过它或许它在Django中的某种普遍的魔法?
如果我要实现相同的查询,我会认为遵循代码更自然(从Django的角度来看,而不是从业务角度来看):
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段中,它提到:
它也是倒退的。引用&#34;反向&#34;关系,只是 使用模型的小写名称。
我之前从未使用过此向后查询。所以我第一次看到它时,就撞了我的大脑。对不起,这个帖子结果证明是愚蠢的。但我希望它可以帮助那些跳过该段中非常(细)线的人。
答案 0 :(得分: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-debug-toolbar或django shell查看查询:
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。 Band没有音乐家对象并不重要;你能给django一个明确的要求: 我想要所有的乐队对象,那里有音乐家与乐队联系,名字叫“Ringo Starr”。
我认为你可能会想到对象封装(例如,乐队没有音乐家;你怎么能根据它进行过滤)?它不是黑魔法,因为过滤器请求是明确且明确的 - 并且作为参数而不是Band对象的成员;但要过滤的命令。例如,你可以做Band.objects(name__icontains = "The")
,即使Band中没有name__icontains
个对象。 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作为数据库;该命令取决于数据库后端):
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 '
答案 1 :(得分:1)
或许可以查看https://docs.djangoproject.com/en/dev/topics/db/queries/的答案。通常使用连接。