django_models_关系多对多
多对多关系
Field类型一样:在模型当中把它做为一个类属性包含进来。
ManyToManyField 需要一个位置参数:和该模型关联的类。
Pizza有多个topping,下面是如何表示这个关系:
from django.db import models class Topping(models.Model): # ... pass class Pizza(models.Model): # ... toppings = models.ManyToManyField(Topping)
与尚未定义的模型的关联关系。
toppings)。
ManyToManyField 并不重要,在两个模型中任选一个即可 —— 不要两个模型都设置。
注意:
Pizza 的表单中将允许用户选择不同的Toppings。
提示:
完整的示例参见多对多关联关系模型示例。
这些选项有助于确定关系如何工作;都是可选的。
¶
但是,有时你可能需要关联数据到两个模型之间的关系上。
但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。
对于上面的音乐小组的例子,代码如下:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): # __unicode__ on Python 2 return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): # __unicode__ on Python 2 return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
这个显式声明定义两个模型之间是如何关联的。
中介模型有一些限制:
- Person)。
- through_fields,否则将引发一个验证错误。
- 模型字段参考)。
你要做的就是创建中介模型的实例:
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, ... date_joined=date(1962, 8, 16), ... invite_reason="Needed a new drummer.") >>> m1.save() >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>]> >>> ringo.group_set.all() <QuerySet [<Group: The Beatles>]> >>> m2 = Membership.objects.create(person=paul, group=beatles, ... date_joined=date(1960, 8, 1), ... invite_reason="Wanted to form a band.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
与常规的多对多字段不同,不能使用add()
,create()
或set()
创建关系:
>>> # 下列语句都是无法工作的 >>> beatles.members.add(john) >>> beatles.members.create(name="George Harrison") >>> beatles.members.set([john, paul, ringo, george])
此时,唯一的办法就是创建中介模型的实例。
remove()调用将不能提供足够的信息,说明应该删除哪个中介模型实例:
>>> Membership.objects.create(person=ringo, group=beatles, ... date_joined=date(1968, 9, 4), ... invite_reason="You've been gone for a month and we miss you.") >>> beatles.members.all() <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]> >>> # This will not work because it cannot tell which membership to remove >>> beatles.members.remove(ringo)
但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系: >>> # Beatles have broken up >>> beatles.members.clear() >>> # Note that this deletes the intermediate model instances >>> Membership.objects.all()
通过创建中介模型的实例来建立对多对多关系后,你就可以执行查询了。 和普通的多对多字段一样,你可以直接使用被关联模型的属性进行查询: # Find all the groups with a member whose name starts with 'Paul' >>> Group.objects.filter(members__name__startswith='Paul') <QuerySet [<Group: The Beatles>]>
如果你使用了中介模型,你也可以利用中介模型的属性进行查询: # 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)) <QuerySet [<Person: Ringo Starr]>
如果你需要访问一个成员的信息,你可以直接获取Membership模型: >>> ringos_membership = Membership.objects.get(group=beatles, person=ringo) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'
另一种获取相同信息的方法是,在Person对象上查询多对多反向关系: >>> ringos_membership = ringo.membership_set.get(group=beatles) >>> ringos_membership.date_joined datetime.date(1962, 8, 16) >>> ringos_membership.invite_reason 'Needed a new drummer.'