代表Django模板中的对象树

时间:2013-10-05 02:14:42

标签: python django templates tree

我有一个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()函数。但就像我说的,我不知道如何把它变成某种单一的数据结构传递给我的模板。

思考?我想我可能想要建立一种字典,我只是无法在这里完成。

2 个答案:

答案 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 answerthis 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)