如何在Django模板中访问多对多"中间表"的属性?

50 投票
3 回答
35913 浏览
提问于 2025-04-16 02:03

来自Django文档的内容...

当你只需要处理简单的多对多关系,比如把比萨和配料混合搭配时,使用一个普通的ManyToManyField就足够了。不过,有时候你可能需要在两个模型之间的关系中关联一些额外的数据。

举个例子,想象一个应用程序用来跟踪音乐家属于哪些乐队。这里,个人和他们所属的乐队之间是多对多的关系,所以你可以用ManyToManyField来表示这种关系。但是,关于成员资格你可能想收集很多细节,比如这个人加入乐队的日期。

在这种情况下,Django允许你指定一个模型来管理这个多对多关系。这样你就可以在中间模型上添加额外的字段。中间模型通过“through”参数与ManyToManyField关联,指向那个充当中介的模型。以音乐家的例子来说,代码大概是这样的:

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来使用你的中间模型(在这个例子中是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()
[<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

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

我应该把乐队对象传给模板吗?还是说我以某种方式传递成员对象?

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

谢谢。

3 个回答

0

Rory Hart的回答基本上是对的。它让你可以在多对多关系中使用中间表(through)。

不过,模板中的循环应该按照cji的建议进行修改,使用select_related,这样可以避免多次查询数据库,减少不必要的开销:

<h2>{{ group.name }}</h2>
{% for membership in group.membership_set.select_related %}
    <h3>{{ membership.person.name }}</h3>
    {{ membership.date_joined }}
{% endfor %}
7

我不确定这是不是唯一的解决办法,但把关系对象传给模板肯定是有效的。在你的视图中,获取会员对象的查询集:

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()

43

最简单的方法就是把这个“乐队”传递给模板。模板可以处理模型之间的关系,而在“组”这个模型上,有两个可以用来查询的管理器,一个是成员(members),另一个是会员集合(membership_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 %}

撰写回答