Hacker News new | ask | show | jobs
by seanwilson 6 days ago
For Django's default template language, does it still have these limitations?

1. Brackets aren't allowed to help with boolean expressions like {% if a and (b or c) %}

2. You can't do basic arithmetic like {{ x * 2 }}, but you're allowed to do {{ x | add:"2" }}. There's hacks to multiply using {% widthratio a 1 b %} or division with {% widthratio a b 1 %} though (https://stackoverflow.com/questions/18350630/multiplication-...).

3. You can't assign expressions to variables like {% with x = a or b %}, so you have to repeat yourself.

4. You can't capture HTML generated from template code in a variable to pass into a partial template to write slots-style HTML components e.g. {% capture x %}Hello {{ username }}{% endcapture %}{{ include "partials/header.html" with body_html=x }}.

5. You can't pass variables to model methods.

I understand there's a philosophy that templates shouldn't contain complex logic, but I find the above pretty arbitrary and leads to code that's harder to maintain. Addition is okay but not multiplication? Boolean logic is okay but not with brackets? I often have to puzzle out some way to get my code to work that goes against what I'd normally want to do, some I'm forced to duplicate template code because you can't put expressions in variables or move basic one-off logic into views (which has poor locality https://htmx.org/essays/locality-of-behaviour/ and makes it harder to move template snippets between pages).

It's like hiding the kitchen knives because they might be misused.

Is Jinja2 a practical alternative or there's friction to using it?

3 comments

> Is Jinja2 a practical alternative or there's friction to using it?

jinja2 is drop in by changing the template backend. You can actually run both at the same time (just can't mix them, ofc).

https://docs.djangoproject.com/en/6.0/topics/templates/

It's a practical alternative, but definitely not a drop-in replacement! I've been working on migrating a django project to jinja2 lately, see the diff: https://framagit.org/la-chariotte/la-chariotte/-/merge_reque...

A few notables differences:

- many controls are now functions/variables (that's good!), and some change name, for example `{% csrf_token %}` -> `{{ csrf_input }}`

- calling methods without parenthesis does not work (that's good!), for example `query.all` -> `query.all()`

- in tests` response.context` is replaced by `response.context_data`, which is not set when calling `render` directly (need to return a proper `TemplateResponse`)

- template folders need to be renamed from `templates` to `jinja2`; there may be a way to change this behavior, but i did not find it, and this change is written so small in the docs i lost an entire day over it

Yea, I was unclear. That's what I meant by "you can't mix them"... just meant using jinja2 is drop it for the backend.

I believe you just set the dirs property in the backend config to look in whatever directory / directories you want it just defaults to jinja2 as you discovered.

From what i understand `DIRS` is only for the top-level app. I couldn't find a way to apply it to inner apps (eg. `auth`, `order`, `etc`). I'd be happy if you know the way i don't have to rename all the template folders!
Thanks! Any noticeable downsides?
For now, no. But to be fair, it hasn't reached prod yet. So far, only great upsides: developer experience (in templates) is considerably better, and djlint/j2lint make it even better (one for HTML validation through the templating, the other for jinja syntax).

Also, it should be considerably faster to render, but i haven't properly measured yet. I just know on lower hardware, the django template "static" homepage (rendered, but no conditionals and no DB calls) takes average 23ms to serve with variations 14-53ms, while a real static page takes 3ms average with 1-6ms (that's all on localhost so no network instability is measured). Can't wait to have the jinja stats!

The migration is fairly simple and well documented.

The problem may be the lack of jinja support for 3rd party django apps (mostly legacy). So make sure they support it first.

> It's like hiding the kitchen knives because they might be misused.

I used to agree with this more strongly but over time decided the Django model was actually more effective as your cue to ask whether you should have been writing a Python function instead and calling it (which has been super easy since something like 1.4 or so IIRC).

Unless you have a small, very diligent development team it always seemed to end up with people coming up with thickets of template logic which were harder to understand and test but because they’d grown so complex people would act like it’d be an excessive amount of work to switch. I had a few projects where I delivered order of magnitude performance improvements while replacing hundreds of lines of ugly code, especially since using Python directly made things like caching and more complex formatting much easier.

> over time decided the Django model was actually more effective as your cue to ask whether you should have been writing a Python function instead and calling it

I get the motivation, but is disallowing brackets and proper variables really the right way to do this? These make code clearer to read and reduce duplication more than they introduce complexity so this is going too far in my opinion and feels arbitrary.

Oh, believe me, I’ve been on both sides there. My observation was that over time the burden of maintenance and on-boarding new developers pushed me from wanting more flexibility to saying that the restriction was more valuable than I thought.