• 【Django】model模型—字段关联关系:多对多


    多对多

    例子:如果 Pizza 含有多种 Topping (配料) – 也就是一种 Topping 可能存在于多个 Pizza 中,并且每个 Pizza 含有多种 Topping。

    from django.db import models
    
    # 配料
    class Topping(models.Model):
        # ...
        pass
    
    
    class Pizza(models.Model):
        # ...
        # Topping为关联模型
        toppings = models.ManyToManyField(Topping)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ManyToManyField字段名最好是关联的模型名的复数形式,以表示所关联模型的集合,需要添加一个位置参数,即关联的模型类名。
    ManyToManyField字段可以在任何一个模型中添加,但不能同时在两模型中添加该字段。通常添加到更符合直观印象的模型当中。如上例中披萨中添加多种配料。
    如果只是为了存储两个模型之间的多对多关系,上述方式就足够使用,但是如果需要在多对多的关系中存入其他数据,则需要一个中间模型发挥作用,在使用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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    中间模型的限制条件:

    • 中间模型M与两个多对多关系的模型A和B之间有且仅有一个指向A和指向B的外键,或者在ManyToManyField字段中指定through_fields参数。
    • 多对多关系中如果是自己指向自己,则中间模型中可以有指向同一个模型的两个外键,代表关系的两端(对比第一点中的AB),但是如果外键数量超过2个,则必须指定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>]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    还可以使用 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)}
    ... )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果由中介模型定义的自定义中介表不对 (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>]>
    >>> # remove()删除所有关联ringo的实例
    >>> beatles.members.remove(ringo)
    >>> beatles.members.all()
    <QuerySet [<Person: Paul McCartney>]>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    clear() 方法可以用来删除一个实例的所有多对多关系:

    >>> # Beatles解散了
    >>> beatles.members.clear()
    >>> # 删除所有相关关系
    >>> Membership.objects.all()
    <QuerySet []>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以通过中间模型的属性进行查询:

    # 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]>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    多对多反向关系查询:

    >>> 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.'
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    新手下白对Latex下手啦!
    【实例分割】YOLACT++:Better Real-time Instance Segmentation论文详解
    基于YOLOv8的手机摄像头的自动检测系统
    【Rust 笔记】14-集合(上)
    怎么解决小程序里引入网络字体包后小程序模拟器上能够正常加载显示而真机上却加载不了显示不出来的问题
    java计算机毕业设计汽车美容管理(附源码、数据库)
    发现 Cargo 的魅力:优雅地构建、发布和管理 Rust 项目
    MC33665 + MC33774 控制流程及 TPL3 帧结构介绍
    一、Unity环境安装
    开发板通过网线连接电脑而上网
  • 原文地址:https://blog.csdn.net/chengyikang20/article/details/136333155