處理類似搭配 pizza 和 topping 這樣簡(jiǎn)單的多對(duì)多關(guān)系時(shí)衣式,使用標(biāo)準(zhǔn)的ManyToManyField 就可以了糙箍。但是渤愁,有時(shí)你可能需要關(guān)聯(lián)數(shù)據(jù)到兩個(gè)模型之間的關(guān)系上。
例如倍靡,有這樣一個(gè)應(yīng)用猴伶,它記錄音樂家所屬的音樂小組课舍。我們可以用一個(gè)ManyToManyField 表示小組和成員之間的多對(duì)多關(guān)系塌西。但是,有時(shí)你可能想知道更多成員關(guān)系的細(xì)節(jié)筝尾,比如成員是何時(shí)加入小組的捡需。
對(duì)于這些情況,Django 允許你指定一個(gè)中介模型來定義多對(duì)多關(guān)系筹淫。 你可以將其他字段放在中介模型里面站辉。源模型的ManyToManyField 字段將使用through 參數(shù)指向中介模型。對(duì)于上面的音樂小組的例子损姜,代碼如下:
既然你已經(jīng)設(shè)置好ManyToManyField 來使用中介模型(在這個(gè)例子中就是Membership)饰剥,接下來你要開始創(chuàng)建多對(duì)多關(guān)系。你要做的就是創(chuàng)建中介模型的實(shí)例:
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>]
與普通的多對(duì)多字段不同摧阅,你不能使用add汰蓉、 create和賦值語句(比如,beatles.members = [...])來創(chuàng)建關(guān)系:
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]
為什么不能這樣做棒卷? 這是因?yàn)槟悴荒苤粍?chuàng)建 Person和 Group之間的關(guān)聯(lián)關(guān)系顾孽,你還要指定 Membership模型中所需要的所有信息祝钢;而簡(jiǎn)單的add、create 和賦值語句是做不到這一點(diǎn)的若厚。所以它們不能在使用中介模型的多對(duì)多關(guān)系中使用拦英。此時(shí),唯一的辦法就是創(chuàng)建中介模型的實(shí)例测秸。
remove()方法被禁用也是出于同樣的原因疤估。但是clear() 方法卻是可用的。它可以清空某個(gè)實(shí)例所有的多對(duì)多關(guān)系:
Beatles have broken up
beatles.members.clear()
Note that this deletes the intermediate model instances
Membership.objects.all()
[]