Chen Yangjian's Blog

Carpe diem - Seize the day

Django 模板里头的递归

| Comments

有的时候,特别是实现类似 reddit.com 的回帖形式,想要树形显示数据,可以用递归的话就会很方便。然而 Django 模板系统的设计方针之一,就是抽离程序内部逻辑,结果抽离得好彻底,直接搞了一个标签(Tag)与过滤(Filter)的解释系统,让程序员们自己玩。

一般情况下,用用系统自带的也就够了,简化的模板系统确实对设计师会更为友好一些。但是牵扯到递归之类的实现,就麻烦了。第一次做的时候,可能想把模块拆出来,递归地去 include,像这样:

{# filename: list.html #}

{% for post in thread %}
   - {{ post.title }}
   {% if post.relies %}
   {% include list.html %}
   {% endif %}
{% endfor %}

但是这样是不行的,因为 Django 的模板解析有两个过程,编译(Compilation)与渲染(Render)。在编译的时候,post 到底有没有 relies 是无从知道的,递归也就无法开始或者结束。解决方式是:

{# filename: list.html #}

{% for post in thread %}
  - {{ post.title }}
  {% with "list.html" as filename %}
  {% with post.replies as thread %}
  {% include list.html %}
  {% endwith %}
  {% endwith %}
{% endfor %}

就差不多了。这个办法,来自 elsdoerfer.name

但是介绍此办法的作者对此也表示担忧,认为产品级的应用不应该使用这样的奇技淫巧,因为需要考量性能方面的问题(重复访问同一个文件)。按照作者的建议,也可以将数据前期在 view 里头准备好,再用模板显示出来。也有人觉得费时,认为还不如实现个 recurse 标签来得爽快。

Django 模板的标签与过滤开发规范写得很详细,可惜我愚钝,只会写点简单的 filter,复杂如 recurse 的标签,一直偷懒没去仔细研究,所以在 lomoo 里头,还是用的第一个办法凑合。先记到这里,回头再去搞吧。

Comments