A Pony and a Walrus Walk into a Library
🦭 + 🐴 + 🏛️ = ❓
TLDR;
I made a library that adds the walrus operator (:=) to Django templates:
django-walrus.
The walrus operator
The walrus operator := (officially called an
"assignment expression") was introduced to Python in the 3.8 release (October 2019).
(It's called "walrus" because := looks like the eyes and the tusks of a
walrus, in case you were wondering)
One way to think about this operator is that it's kind of like a regular assignment with =, but
it also "returns" the value it just assigned. So you can assign a value and test against it in a single
step, like so:
# original code
count = Article.objects.filter(title__icontains="Django").count()
if count:
print(f"Found {count} matching articles")
else:
print("No matching article found")
# rewritten with the walrus operator
if count := Article.objects.filter(title__icontains="Django").count():
print(f"Found {count} matching articles")
else:
print("No matching article found")
There's a lot more to it (the PEP that introduced the new feature is 1300 lines long! 😮), but that's the basics. Now let's see how Django gets into the mix.
Walruses in Django templates
Seeing those examples of how the walrus operator can simplify if statements in Python got me
thinking. Django templates have {% if %} too, and they kind of have assignments with
{% with %}. So could we somehow use the walrus operator there?
Let's say you have the following template that calls out to a
some_complicated_function() that's not particularly efficient (maybe it makes some complez
database queries for example, or it calls out to an external API, ...).
{% if mymodel.some_complicated_function %}
Result: {{ mymodel.some_complicated_function }}
{% endif %}
That's not really ideal because the function is called twice. We can rewrite using
{% with %} to store the result in a variable like this:
{% with result=mymodel.some_complicated_function %}
{% if result %}
Result: {{ result }}
{% endif %}
{% endwith %}
Now the function is only called once which is better, but that's a lot of extra lines. Wouldn't it be better if we could do:
{% if result := mymodel.some_complicated_function %}
Result: {{ result }}
{% endif %}
Introducing django-walrus
That's exactly what my library django-walrus does!
Install it with pip install django-walrus, add "walrus" to your
settings.INSTALLED_APPS, and the new syntax is magically avaible in your Django templates.
The code for it is very simple, but also quite hackish (it uses some internal APIs which are not guaranteed to continue existing as Django is updated):
from django.template import smartif
def _walrus_op(context, x, y):
result = y.eval(context)
context[x.value.var.var] = result
return result
smartif.OPERATORS[':='] = smartif.infix(11, _walrus_op)
smartif.OPERATORS[':='].id = ':='
It turns out that Django's {% if %} tag (whose code is located in the
smartif) uses a convenient
operator dictionary
at its toplevel. This makes it very easy to add custom operators to the template language.
For my library I packaged the whole thing in an AppConfig.ready() so that the monkeypatching
happens automatically when the app is loaded, and that was it.