如何使用django模板渲染树结构(递归)?

Dav*_*kes 55 python django

我在内存中有一个树结构,我想使用Django模板在HTML中呈现.

class Node():
  name = "node name"
  children = []
Run Code Online (Sandbox Code Playgroud)

将有一些对象root是a Node,并且childrenNodes 的列表.root将在模板的内容中传递.

我已经找到了关于如何实现一点的讨论,但是海报表明这在生产环境中可能并不好.

有人知道更好的方法吗?

Roh*_*han 69

使用with模板标签,我可以做树/递归列表.

示例代码:

主模板:假设'all_root_elems'是一个或多个树根的列表

<ul>
{%for node in all_root_elems %} 
    {%include "tree_view_template.html" %}
{%endfor%}
</ul>
Run Code Online (Sandbox Code Playgroud)

tree_view_template.html呈现嵌套ul,li并使用node模板变量,如下所示:

<li> {{node.name}}
    {%if node.has_childs %}
        <ul>
         {%for ch in node.all_childs %}
              {%with node=ch template_name="tree_view_template.html" %}
                   {%include template_name%}
              {%endwith%}
         {%endfor%}
         </ul>
    {%endif%}
</li>
Run Code Online (Sandbox Code Playgroud)

  • 虽然我确信没有充分的理由不按要求做,但这个答案实际上解决了这个问题.存在性能损失,但使用{%with%}将模板名称存储在变量中可防止django模板编译器无限递归. (3认同)

And*_*ius 28

我认为规范的答案是:"不要".

你可能应该做的是解开你的视图代码中的东西,所以这只是在模板中迭代(in | de)凹痕的问题.我想我会通过在列表中附加缩进和dedents来实现它,同时通过树递归然后将"travelogue"列表发送到模板.(然后模板将插入<li></li>从该列表中创建具有"理解"它的递归结构.)

我也很确定递归包含模板文件真的是一种错误的方法...

  • 当然.你创建一个像['in','in','blah','out','blah','out']的列表,然后你在模板中循环它.如果它等于'in'你发出一个li,'out'你发出一个/ li,否则你只是转储文本本身. (11认同)
  • 我没有看到这可能如何保留原始数据的层次结构,除非您在视图中将整个事物渲染为HTML.你能提供一个更具体的例子吗? (4认同)
  • 关于为什么递归构建菜单“真的错了”的任何说明?真的有那么贵吗? (2认同)
  • 虽然我同意分离逻辑/表示。这是出现问题的罕见情况之一。因为它是一个递归的东西被显示。逻辑/表示划分的问题之一是在哪里划线,这样它实际上不会引起太多耦合,因为它需要控制器中的复杂逻辑才能使简化视图工作。理论上,控制器不应该理解视图,只需要知道如何传递数据即可。但我想这只是与现实的妥协之一。 (2认同)
  • 这不应该是公认的答案。创建一个简单的包含模板标签很简单,该标签递归地包含自身以将信息树显示为 html。只要每个递归调用不调用数据库,并且只要实现了适当的缓存,就不会出现任何性能问题。尝试在视图中将正确的 html 编写为连接字符串要麻烦得多;html 代码属于模板。 (2认同)

小智 20

这可能比你需要的更多,但是有一个名为'mptt'的django模块 - 它在sql数据库中存储一个分层树结构,并包含在视图代码中显示的模板.你可能会在那里找到有用的东西.

这是链接:django-mptt


Art*_*ult 18

我来不及)所有的你用这么多的不必要的标签,这是我该怎么办recuesive:

在主模板中:

<!-- lets say that menu_list is already defined -->
<ul>
    {% include "menu.html" %}
</ul>
Run Code Online (Sandbox Code Playgroud)

然后在menu.html中:

{% for menu in menu_list %}
    <li>
        {{ menu.name }}
        {% if menu.submenus|length %}
            <ul>
                {% include "menu.html" with menu_list=menu.submenus %}
            </ul>
        {% endif %}
    </li>
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,这是这个线程中最干净和最好的抽象。不错的亚瑟! (2认同)

Vla*_*mir 12

是的,你可以做到.这是一个小技巧,将文件名作为变量传递给{%include%}:

{% with template_name="file/to_include.html" %}
{% include template_name %}
{% endwith %}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ohn 9

对于这个确切的场景,Django有一个内置的模板助手:

https://docs.djangoproject.com/en/dev/ref/templates/builtins/#unordered-list

  • 如果要输出的所有内容都是"<li> sometext </ li>",则可以正常工作.如果您有更复杂项目的嵌套层次结构,并且您(例如)希望每个项目都是链接,则此标记无用. (7认同)

dis*_*cer 9

我有同样的问题,我写了一个模板标签.我知道还有其他像这样的标签,但我还是需要学习制作自定义标签:)我认为结果非常好.

阅读docstring了解使用说明.

github.com/skid/django-recurse