除非回复可以是对多个帖子的回复,否则ManyToManyField 不是您想要的。你只需要一个ForeignKey:
class Discussion(models.Model):
message = models.TextField()
reply_to = models.ForeignKey('self', related_name='replies',
null=True, blank=True)
然后您可以通过Discussion.replies 获得讨论的回复。
不幸的是,没有办法在 Django 的模板语言中进行递归,因此您必须 1) 运行递归函数以获取“扁平化”回复列表,并将其放入上下文中,或者 2) 编写一个可以递归调用的函数,该函数使用模板生成每个级别,如下所示:
_DiscussionTemplate = Template("""
<li>{{ discussion.message }}{% if replies %}
<ul>
{% for reply in replies %}
{{ reply }}
{% endfor %}
</ul>
{% endif %}</li>
""".strip())
class Discussion(models.Model):
message = models.TextField()
reply_to = models.ForeignKey('self', related_name='replies',
null=True, blank=True)
@property
def html(self):
return _DiscussionTemplate.render(Context({
'discussion': self,
'replies': [reply.html() for reply in self.replies.all()]
}))
然后在您的顶级模板中,您只需要:
<ul>
{% for d in discussions %}
{{ d.html }}
{% endfor %}
</ul>
根据需要应用 CSS 使其看起来不错。
编辑: 根讨论是Discussion.objects.filter(reply_to=None) 中的讨论。所有代码,包括_DiscussionTemplate,都在您的models.py 中。这样,_DiscussionTemplate 在模块加载时会被初始化一次。
编辑 2: 将 HTML 放入模板文件非常简单。将设置_DiscussionTemplate的视图代码更改为:
_DiscussionTemplate = loader.get_template("discussiontemplate.html")
然后创建discussiontemplate.html:
<li>{{ discussion.message }}{% if replies %}
<ul>
{% for reply in replies %}
{{ reply }}
{% endfor %}
</ul>
{% endif %}</li>
根据需要设置模板文件的路径。