我有一个Django模型,它对同一个类有一个ForeignKey,有效地创建了一个树:
class Tag(models.Model):
name = models.CharField(max_length=50)
parent = models.ForeignKey('self', blank=True, null=True)
在Django shell(./manage.py shell
)中使用递归,我可以很容易地将树表示为纯文本:
def nodes(parent, level):
children = Tag.objects.filter(parent=parent)
for c in children:
spaces = ""
for i in xrange(0,level):
spaces+=" "
print "%s%s" % (spaces,c.name)
nodes(c.pk,level+1)
nodes(None,0)
我不确定的是如何将整个树放入Django模板中。我已经创建了一个自定义模板标签,以使这更容易,但我无法弄清楚如何将数据传递到模板,以轻松迭代树在模板中显示。这是基本的模板标签。
@register.inclusion_tag("core/tags.html")
def render_tags(**kwargs):
tags = Tag.objects.all()
return {"tags":tags}
我知道以上是非常基本的,我不知道从哪里开始。我认为如果Tag类有一个函数来获取它的子节点可能会更容易,所以我也有类:
def children(self):
return Tag.objects.filter(parent=self.pk)
我在那里使用self.pk
,然后树的根只是rootTag=Tag()
,因为它没有pk,因为它没有保存,rootTag.children()
会找到任何没有的标签父标记,然后任何这些标记可以继续调用它们的children()
函数。但就像我说的,我不知道如何把它变成某种单一的数据结构传递给我的模板。
思考?我想我可能想要建立一种字典,我只是无法在这里完成。
答案 0 :(得分:8)
正如Jproffitt所提到的那样Django MPTT是实现目标的好方法 你要。
使用它,您可以在模板中递归访问子实例, 像这样:
{% load mptt_tags %}
<h1>Tags</h1>
<ul>
{% recursetree tags %}
<li>{{ node.name }}
{% if not node.is_leaf_node %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
我有一个项目,它很容易设置和使用。
如果您不想使用专用应用,我认为这些解决方案可能会有效 (另):
# yourapp/tags_list.html
<ul>
{% for tag in tags %}
<li>{{ tag.name }}</li>
{% if tag.children.exists %}
{% with tag.children.all as tags %}
{% include "yourapp/tags_list.html" %}
{% endwith %}
{% endif %}
{% endfor %}
</ul>
这样,模板应该只是递归地调用自己,直到没有 更多儿童标签。此解决方案需要您在Tag模型中指定相关名称:
parent = models.ForeignKey('self', blank=True, null=True, related_name="children")
但是,请注意,这些解决方案将涉及比使用MPTT更多的数据库查询。
答案 1 :(得分:6)
我搜索的越多越多,我意识到尝试用模板递归来做这件事不是可行的方法,但我现在仍然想避免使用django-mptt,因为它似乎对我来说太多了感觉是一个简单的问题,我不打算拥有非常大的树木。
An answer到this related question注意到模板递归确实不应该被使用,而是在视图中处理序列化。我原本认为这会更好,因为我在最初的问题中提到过,假设我最需要建立一个字典。
This answer基本上就是我想要的。 serializable_object
是我可以传递到模板中的东西,如果我决定稍后使用json,那么很容易就像那个答案中提到的那样。
我将选择艾略特的答案是正确的,因为django-mptt可能是这里做的“最正确”的事情,但对于任何想要避开另一个第三方应用的人来说,这是一个替代解决方案。
class Tag(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=100, blank=True)
parent = models.ForeignKey('self', blank=True, null=True)
# Not necessary, can use Tag.tag_set.all()
# But this function uses pk which allows
# rootTag=Tag()
# rootTag.children()
# Because rootTag has no pk
def children(self):
return Tag.objects.filter(parent=self.pk)
def serializable_object(self):
obj = {'name': self.name, 'children': []}
for child in self.children():
obj['children'].append(child.serializable_object())
return obj
使用该模型,您可以执行以下操作:
rootTag=Tag()
rootTag.serializable_object()
或者:
tags = []
for t in Tag.objects.filter(parent=None):
tags.append(t.serializable_object())
# And if you need JSON,
from django.utils import simplejson
simplejson.dumps(tags)