Recursion in Django templates

Recently, someone asked in #django about outputting recursive structures, say a forum or comment thread. Although this could be considered a presentation logic issue, the smartest thing is most likely to prepare the data in your view accordingly. Besides, Django’s template language doesn’t support recursion. Or does it?

There are no macros, or a call/function concept. And if you attempt a standard recursive {% include %} you’ll notice that requests hang, as the tag is resolved during compilation – the context is still out of the picture, so there is no way to stop the recursion. But if you look at the source you’ll notice that includes are actually implemented differently if the filename is passed as a variable. A context is now needed to resolve it, and the included file is loaded while rendering (see IncludeNode vs ConstantIncludeNode).

Kind of neat, I think – although I wouldn’t recommend actually using that approach in a production app, and if it’s only for the fact that the template is reloaded from disk each and every time, which I can’t imagine is good for performance. Maybe you should use it. Doesn’t Django use a cached loader now? Make your own decision.

I would now say it’s mainly problematic since it relies on an implementation detail in the Django template language. There’s no guarantee this might not stop working in a new version.

As for performance issue, I was probably too alarmist, especially now that Django actually can cache templates in memory once compiled. So you probably shouldn’t worry.

But still, if you do want to go after performance, and you are rendering out a forum thread with a huge number of branches and nodes, it’ll sure be faster to set something like an indentation-level attribute in your python code.

Awesome work. Was so proud of my recursion in the view and got stuck on the rendering of it in the template. Can’t believe that you have to assign the filename to a variable but it works just like you said. Thanks, you’re a legend! 🙂

PS! I’m using it on a production system but just for admin/cms stuff, so I guess there’s no real harm there. Wanted to have a menu using this (one of those nice superfish-style dropdown menus), but I suspect that that would REALLY kill the server (having to resolve this recursion on every single page call).

I put the code in a template which I included in my main template, the first include is fine, but it doesn’t go further.
It react like if “post” from “{% for post in thread %} ” is empty (but it’s not) and doesn’t go inside the loop.

Jérémie, I’m having this exact same issue while trying to get threaded-comments[1] rendering working.

Other directions I’ve been going are to integrate a “recursive_include” into my template tags based on the function at this bug request [2]. That seemed to cause the same problems, so I’m going in a new direction based on [3], which is an intricate dance between template and view to create the final template.

You wouldn’t think getting threaded-comments to actually display in a nested structure would be this difficult!

This still works like a charm years later, I also find it more elegant than doing it in the view, even though we might encounter performance issues.
It’s a template that isn’t used that much on the site anyway.
I have a suggestion to make the rendered code more beautiful and avoid having empty blocks when the node has no children.
Put a if test before the include block {% include filename %} or you could also put it at the beginning of the template, so it doesn’t render with nothing inside.

Thanks! I’m not yet to the point where I actually need a recursive template, but I’ve used what you shared in my template; I’ll have to wait until I get more data added to find out if it works like I’m hoping.