【Django杂记】2、Django3.2的ManyToManyField(多对多)中的through的作用
我们使用ManyToManyField自动生成第三张表,如果我们想要对自动生成的第三张表做一些额外的字段,对于这些情况,Django允许你指定用于控制多对多关系的模型,你可以在中间模型当中添加额外的字段,在ManyToManyField的时候使用through参数指定多对多关系使用哪个中间模型
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
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)- 你需要在设置中间模型的时候,显式地为多对多关系中涉及的中间模型指定外键;这种显式声明定义了这两个模型之间是如何关联的。
- 在中间模型当中需要有一些限制:
- 如果你的中间模型当中,可以有两个指向同一个模型的外键,但这两个外键分别代表多对多关系(不同)的两端。如果外键的个数超过两个,那么你必须通过ManyToManyField.through_fields参数显式的指向外键名,如下:
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField('Book',through='AuthorBook',through_fields=['author','book']
class AuthorBook(models.Model):
author = models.ForeignKey(Author,related_name='a' ,on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
tuijian = models.ForeignKey(Author,related_name='b',on_delete=models.CASCADE)
# 参数related_name:用来区分同一个表的外键,方便之后用来反向查询- 现在已经通过中间模型完成你的ManyToManyField(例子中的Membership),开始创建一些多对多关系了。你可以通过实例化中间模型来创建关系:
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()创建关系,只要你为任何必填的字段指定through_defaults
beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})- 如果自定义中间没有强制(model1, model2)对的唯一性,调用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 deletes both of the intermediate model instances for Ringo Starr
beatles.members.remove(ringo)
beatles.members.all()
--> <QuerySet [<Person: Paul McCartney>]>- 方法clear()用于实例的所有多对多关系
beatles.members.clear()
# Note that this deletes the intermediate model instances
Membership.objects.all()
--> <QuerySet []>- 一旦你建立了自定义多对多关联关系,就可以执行查询操作。和一般的多对多关联关系一样,你可以使用多对多关联模型的属性来操作
Group.objects.filter(members__name__startswith='Paul')
--> <QuerySet [<Group: The Beatles>]>- 当你使用中间模型的时候,你也可以查询他的属性
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.'本文来自博客园,作者:郭祺迦,转载请注明原文链接:https://www.cnblogs.com/guojie-guojie/p/16194561.html

浙公网安备 33010602011771号