2

我有一个 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()调用它们的函数. 但就像我说的,我不知道如何将其转换为某种单一的数据结构以传递给我的模板。

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

4

2 回答 2

9

正如 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 更多的数据库查询。

于 2013-10-05T07:47:01.933 回答
6

我搜索得越多,我就越意识到尝试使用模板递归来做到这一点并不是要走的路,但我现在仍然想避免使用 django-mptt,因为它对于我的感觉来说似乎太多了。简单的问题,我不打算拥有非常大的树。

这个相关问题的回答指出,确实不应该使用模板递归,而是在视图中处理序列化。正如我在原始问题中提到的那样,我认为这会更好,假设我最有可能需要构建一个字典。

这个答案基本上就是我想要的。这serializable_object是我可以传递给模板的东西,如果我决定以后要使用 json ,那么按照那个答案中的说明很容易做到。

我将选择 Eliot 的答案作为正确的答案,因为 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)
于 2013-10-05T16:44:25.247 回答