• django_models_关系多对多


    多对多关系

    ManyToManyField 用来定义多对多关系, 和使用其它Field类型一样:在模型当中把它做为一个类属性包含进来。

    ManyToManyField 需要一个位置参数:和该模型关联的类。

    例如,一个Pizza可以有多种Topping 即一种Topping 也可以位于多个Pizza上,而且每个Pizza有多个topping,下面是如何表示这个关系:

    from django.db import models
    
    class Topping(models.Model):
        # ...
        pass
    
    class Pizza(models.Model):
        # ...
        toppings = models.ManyToManyField(Topping)

    ForeignKey一样,你还可以创建递归关联关系(与其自身具有多对多关系的对象)和与尚未定义的模型的关联关系

    建议你以被关联模型名称的复数形式做为ManyToManyField 的名字(例如上例中的toppings)。

    在哪个模型中设置 ManyToManyField 并不重要,在两个模型中任选一个即可 —— 不要两个模型都设置。

    注意:

      一般来说,ManyToManyField 实例应该放在要在表单中被编辑的对象中。 在上面的例子中,Topping 位于Pizza 中(而不是在 toppings 里面设置pizzas 的ManyToManyField 字段),因为设想一个Pizza 有多种Topping 比一个Topping 位于多个Pizza 上要更加自然。 按照上面的方式,在Pizza 的表单中将允许用户选择不同的Toppings。

    提示:

      完整的示例参见多对多关联关系模型示例

      ManyToManyField 字段还接受别的参数,在模型字段参考中有详细介绍。 这些选项有助于确定关系如何工作;都是可选的。

    多对多关系的额外字段

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

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

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

    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, on_delete=models.CASCADE)
        group = models.ForeignKey(Group, on_delete=models.CASCADE)
        date_joined = models.DateField()
        invite_reason = models.CharField(max_length=64)
    

      

    在设置中介模型时,要显式地指定外键并关联到多对多关系涉及的模型。 这个显式声明定义两个模型之间是如何关联的。

    中介模型有一些限制:

    • 中介模型必须有且只有一个外键到源模型(上面例子中的Group),或者你必须使用ManyToManyField.through_fields 显式指定Django 应该在关系中使用的外键。 如果你的模型中存在不止一个外键,并且through_fields没有指定,将会触发一个无效的错误。 对目标模型的外键有相同的限制(上面例子中的Person)。
    • 对于通过中介模型与自己进行多对多关联的模型,允许存在到同一个模型的两个外键,但它们将被当做多对多关联中一个关系的两边。 如果有超过两个外键,同样你必须像上面一样指定through_fields,否则将引发一个验证错误。
    • 使用中介模型定义与自身的多对多关系时,你必须设置 symmetrical=False(详见模型字段参考)。

    既然你已经设置好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()创建关系:

    >>> # 下列语句都是无法工作的
    >>> beatles.members.add(john)
    >>> beatles.members.create(name="George Harrison")
    >>> beatles.members.set([john, paul, ringo, george])

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

    remove()方法被禁用也是出于同样的原因。 例如,如果通过中介模型定义的表没有在(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 will not work because it cannot tell which membership to remove
    >>> beatles.members.remove(ringo)
    但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系:
    
    >>> # Beatles have broken up
    >>> beatles.members.clear()
    >>> # Note that this deletes the intermediate model instances
    >>> Membership.objects.all()
    通过创建中介模型的实例来建立对多对多关系后,你就可以执行查询了。 和普通的多对多字段一样,你可以直接使用被关联模型的属性进行查询:
    
    # Find all the groups with a member whose name starts with 'Paul'
    >>> Group.objects.filter(members__name__startswith='Paul')
    <QuerySet [<Group: The Beatles>]>
    如果你使用了中介模型,你也可以利用中介模型的属性进行查询:
    
    # 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]>
    如果你需要访问一个成员的信息,你可以直接获取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.'
  • 相关阅读:
    POJ1185:炮兵阵地(状压dp)
    POJ3254:Corn Fields(状压dp第一发)
    二进制x&(x-1);
    子集和问题(应用--换零钱)POJ2229:Sumsets
    JAVA math包
    UVA+POJ中大数实现的题目,持续更新(JAVA实现)
    HDU中大数实现的题目,持续更新(JAVA实现)
    SDUT中大数实现的题目,持续更新(JAVA实现)
    JAVA与ACM
    HDU3123:GCC(同余模简单题)
  • 原文地址:https://www.cnblogs.com/Xingtxx/p/11010003.html
Copyright © 2020-2023  润新知