中介模型

处理类似搭配 pizza 和 topping 这样简单的多对多关系时,使用标准的ManyToManyField  就可以了。但是,有时你可能需要关联数据到两个模型之间的关系上。

例如,有这样一个应用,它记录音乐家所属的音乐小组。我们可以用一个ManyToManyField 表示小组和成员之间的多对多关系。但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。

对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。源模型的ManyToManyField 字段将使用through 参数指向中介模型。对于上面的音乐小组的例子,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
    
group 
= 
models.ForeignKey(Group)
    
date_joined 
= 
models.DateField()
    
invite_reason 
= 
models.CharField(max_length
=
64
)

既然你已经设置好ManyToManyField 来使用中介模型(在这个例子中就是Membership),接下来你要开始创建多对多关系。你要做的就是创建中介模型的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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
()
[<Person: Ringo Starr>]
>>> ringo.group_set.
all
()
[<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
()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

与普通的多对多字段不同,你不能使用add、 create和赋值语句(比如,beatles.members [...])来创建关系:

1
2
3
4
5
6
# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name
=
"George Harrison"
)
# AND NEITHER WILL THIS
>>> beatles.members 
= 
[john, paul, ringo, george]

为什么不能这样做? 这是因为你不能只创建 Person和 Group之间的关联关系,你还要指定 Membership模型中所需要的所有信息;而简单的addcreate 和赋值语句是做不到这一点的。所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。

 remove()方法被禁用也是出于同样的原因。但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系: 

1
2
3
4
5
>>> 
# Beatles have broken up
>>> beatles.members.clear()
>>> 
# Note that this deletes the intermediate model instances
>>> Membership.objects.
all
()
[]