47

From the Django documentation...

When you're only dealing with simple many-to-many relationships such as mixing and matching pizzas and toppings, a standard ManyToManyField is all you need. However, sometimes you may need to associate data with the relationship between two models.

For example, consider the case of an application tracking the musical groups which musicians belong to. There is a many-to-many relationship between a person and the groups of which they are a member, so you could use a ManyToManyField to represent this relationship. However, there is a lot of detail about the membership that you might want to collect, such as the date at which the person joined the group.

对于这些情况,Django 允许您指定将用于管理多对多关系的模型。然后,您可以在中间模型上放置额外的字段。中间模型与 ManyToManyField 相关联,使用 through 参数指向将充当中介的模型。对于我们的音乐家示例,代码如下所示:

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __unicode__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

现在您已经设置了 ManyToManyField 以使用中间模型(在本例中为会员),您已准备好开始创建一些多对多关系。您可以通过创建中间模型的实例来做到这一点:

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()
[<Person: Ringo Starr>]

ringo.group_set.all()
[<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()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

来源:http ://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

我的问题是,如何设置视图和模板来访问这些附加属性。假设我有一个乐队页面,我想显示乐队名称,遍历会员记录并显示名称和 date_joined。

我应该将带对象传递给模板吗?还是我以某种方式传递会员对象?

我将如何在模板中创建 for 循环?

谢谢。

4

3 回答 3

40

最简单的方法是将乐队传递给模板。模板能够导航模型之间的关系,并且在 Group 上有 members 和 members_set 查询集管理器。所以我会这样做:

看法:

def group_details(request, group_id):
    group = get_object_or_404(Group, pk=group_id)
    return render_to_response('group_details.html',
                              {'group': group})

模板:

<h2>{{ group.name }}</h2>
{% for membership in group.membership_set.all %}
    <h3>{{ membership.person }}</h3>
    {{ membership.date_joined }}
{% endfor %}
于 2010-07-30T05:11:17.493 回答
7

我不确定这是否是唯一的解决方案,但是将关系对象传递给模板确实有效。在您看来,获取 Membership 对象的 QuerySet:

rel = Membership.objects.filter( group = your_group ).select_related()

并将其传递给模板,您可以在其中迭代它{% for %}

{% for r in rel %}
     {{ r.person.name }} joined group {{ r.group.name }} on {{ r.date_joined }}<br />
{% endfor %}

请注意,这不应该执行任何额外的查询,因为select_related().

于 2010-07-30T04:55:07.907 回答
0

罗里哈特的回答大部分是正确的。它允许您through在 ManyToMany 关系中使用中间 ( ) 表。

但是,模板中的循环应该select_related按照 cji 的建议进行修改,以避免使用额外的查询访问数据库:

<h2>{{ group.name }}</h2>
{% for membership in group.membership_set.select_related %}
    <h3>{{ membership.person.name }}</h3>
    {{ membership.date_joined }}
{% endfor %}
于 2020-04-04T10:10:39.923 回答