Django中间模型管理多对多模型

中间模型管理多对多模型

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属性来定义一个中间模型进行管理,注意:

  • 中间模型必须有且仅有一个外键指向目标模型(也就是本例中的Person)。使用多于一个的外键将会引发验证错误。
  • 中间模型必须有且仅有一个外键指向源模型(也就是本例中的Group)。使用多于一个的外键将会引发验证错误。
  • 唯一例外的情况是一个模型通过中间模型与自身产生多对多关系。这种情况下,两个外键指向同一个模型是允许的,但是它们会被看作同一个多对多关系的两个(不同的)方面。

创建

>>> 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(), or 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)})

也可以通过中间模型来创建源模型和目标模型

>>> 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>]>

查询

>>> 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]>

或通过中间模型直接查询

>>> 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.'

或者通过中间模型的反向查询

>>> ringo = Person.object.fitter(name=ringo)
>>> 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.'

删除

  • remove():如果目标模型没有对数据的独一性进行要求,那么remove()将会删除所有相同的值

    >>> beatles.members.all()
    <QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
    >>> beatles.members.remove(ringo)
    >>> beatles.members.all()
    <QuerySet [<Person: Paul McCartney>]>
    

    可以看到本来Queryset对象中有两个相同的<Person: Ringo Starr>,删除后都不存在了

  • clear():删除一个实例的所有中间关系

    >>> beatles.members.clear()
    >>> Membership.objects.all()
    <QuerySet []>
    
posted @ 2020-05-15 11:50  言兴  阅读(281)  评论(0)    收藏  举报