call¶
The call template tag allows functions to be called from within a template.
Note
Make sure to install dj_angles and include {% load dj_angles %} in your template if "dj_angles.templatetags.dj_angles" is not added to template built-ins.
Example¶
Let’s say you have a Book model and you want to list all of the books, but add an icon if the book has been read by the current user.
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
def is_read(self, user):
if user.is_anonymous:
return False
return self.readers.filter(user=user).exists()
class Reader(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
book = models.ForeignKey('Book', related_name='readers', on_delete=models.CASCADE)
# views.py
from django.shortcuts import render
from book.models import Book
def index(request):
books = Book.objects.all()
return render(request, 'index.html', {'books': books})
<!-- index.html -->
{% for book in books %}
<div>
{{ book.title }}
{% call book.is_read(request.user) as is_read_by_current_user %}
{% if is_read_by_current_user %}
✅
{% else %}
❌
{% endif %}
</div>
{% endfor %}
Calling functions¶
The template tag function argument tries to look as similar to normal Python code as possible. If it is followed by the word “as” and then a variable name, the result of the function will be stored in the context for later use.
<!-- index.html -->
{% call slugify('Hello Goodbye') as slug %}
{{ slug }} <!-- hello-goodbye -->
The call template tag only has access to functions that are available in the context. So, the slugify function needs to be added to the context in the view.
# views.py
from django.shortcuts import render
from django.utils.text import slugify
def index(request):
return render(request, 'index.html', {'slugify': slugify})
If as is not used, a stringified result of the function will be output directly in the template.
<!-- index.html -->
{% call slugify('Hello Goodbye') %} <!-- hello-goodbye -->
Traversing¶
Objects¶
# views.py
from django.shortcuts import render
import django
def index(request):
return render(request, 'index.html', {'django': django})
{% call django.utils.text.slugify('Hello Goodbye') as slug %}
{{ slug }} <!-- hello-goodbye -->
Dictionaries¶
# views.py
from django.shortcuts import render
import django
def index(request):
data = {"functions": {"slugify": django.utils.text.slugify}}
return render(request, "index.html", {"data": data})
<!-- index.html -->
{% call data.functions.slugify('Hello Goodbye') as slug %}
{{ slug }} <!-- hello-goodbye -->
Supported argument types¶
Most Python primitives are supported as arguments, e.g. strings, ints, lists, dictionaries, etc.
# views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html', {'add': lambda a, b: a + b})
<!-- index.html -->
{% call add(2, 3) as result %}
{{ result }} <!-- 5 -->
Datetimes¶
There are a few helper methods available to parse datetimes from strings in the django.utils.dateparse module.
# views.py
from django.shortcuts import render
from datetime import timedelta
from django.utils.dateparse import parse_datetime
def index(request):
return render(request, 'index.html', {'add_day': lambda dt: parse_datetime(dt) + timedelta(days=1)})
<!-- index.html -->
{% call add_day("2025-03-14") as next_day %}
{{ next_day|date:"r" }} <!-- Wed, 13 Mar 2025 00:00:00 -->
Template variables¶
Django template variables can be used for args or kwargs (as long as they are available in the context).
# views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html', {'add': lambda a, b: a + b, 'number_one': 1, 'number_two': 2})
<!-- index.html -->
{% call add(number_one, number_two) as result %}
{{ result }} <!-- 3 -->
Note
If a variable is referred to, but is not available in the context a VariableDoesNotExist error will be raised.
Unpacking¶
Lists and dictionaries can be unpacked using the * and ** operators.
Args¶
# views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html', {'add': lambda a, b: a + b, 'numbers': [2, 3]})
<!-- index.html -->
{% call add(*[2, 3]) as result %}
<!-- using template variable -->
{% call add(*numbers) as result %}
<!-- without unpacking -->
{% call add(2, 3) as result %}
{{ result }} <!-- 5 -->
Kwargs¶
# views.py
from django.shortcuts import render
def make_name(first_name, last_name):
return f"{first_name} {last_name}"
def index(request):
return render(request, 'index.html', {'make_name': make_name, 'name': {'first_name': 'Alice', 'last_name': 'Smith'}})
<!-- index.html -->
{% call make_name(**{'first_name': 'Alice', 'last_name': 'Smith'}) as result %}
<!-- using template variable -->
{% call make_name(**name) as result %}
<!-- without unpacking -->
{% call make_name(first_name='Alice', last_name='Smith') as result %}
{{ result }} <!-- Alice Smith -->
Querysets¶
# views.py
from django.shortcuts import render
from book.models import Book
def index(request):
books = Book.objects.all()
return render(request, 'index.html', {'books': books})
<!-- index.html -->
{% call books.filter(published__gte='2020-01-01').order_by('name') as books %}
<ul>
{% for book in books %}
<li>{{ book }}</li>
{% endfor %}
</ul>
How does this work?¶
The call template tag is a custom template tag which parses the first argument into Python AST and then evaluates it. After evaluation, the result is stored in the context with the name specified.