zoukankan      html  css  js  c++  java
  • 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.'
  • 相关阅读:
    phpcms后台进入地址(包含No permission resources错误)
    phpmyadmin上传大sql文件办法
    ubuntu彻底卸载mysql
    Hdoj 2602.Bone Collector 题解
    一篇看懂词向量
    Hdoj 1905.Pseudoprime numbers 题解
    The Python Challenge 谜题全解(持续更新)
    Hdoj 2289.Cup 题解
    Hdoj 2899.Strange fuction 题解
    Hdoj 2199.Can you solve this equation? 题解
  • 原文地址:https://www.cnblogs.com/Xingtxx/p/11010003.html
Copyright © 2011-2022 走看看