Mit den Templatetags, die Django zur Verfügung stellt, lässt sich gut arbeiten. Wirklich interessant ist aber die Möglichkeit eigene Templatetags zu schreiben.
Wir wollen ein Templatetag für unser Projekt schreiben mit dem wir ermitteln können ob ein Benutzer der Autor eines Rezeptes ist. Außerdem soll es die Möglichkeit bieten einen alternativen Block zu rendern, wenn dies nicht der Fall ist.
Das Templatetag dafür soll also so aussehen:
{% is_author user recipe %}
Der Benutzer ist Autor des Rezepts oder Redakteur.
{% else %}
Dieses Rezept darf von diesem Benutzer nicht bearbeitet werden.
{% endis_author %}
Das Templatetag ist is_author. Das erste Argument user ist ein User Objekt. Das zweite Argument recipe ist eine Recipe Instanz. Ansonsten soll das Templatetag wie eine if-Bedingung funktionieren.
Templatetags müssen mit einer bestimmten Verzeichnisstruktur angelegt werden. Im Verzeichnis der Applikation wird ein neues Verzeichnis templatetags erstellt. Darin wird die leere Datei __init__.py angelegt, um das Verzeichnis als Python Package zu markieren. Als letztes legen wir eine Python Datei an, die das Modul für unsere Templatetags ist. Unser erstes Modul nennen wir recipes.py.
recipes/
`-- templatetags
|-- __init__.py
`-- recipes.py
Ein Templatetag besteht immer aus einer Kompilierungsfunktion und einer Node. Die Kompilierungsfunktion parst das Tag mit Hilfe eines Parsers. Als Ergebnis gibt sie eine Instanz der Node zurück. Diese hat eine render-Methode, die die Ausgabe erzeugt.
Zuerst erstellt du die Kompilierungsfunktion in der neu angelegten Datei recipes.py:
from django import template
register = template.Library()
@register.tag(name='is_author')
def do_is_author(parser, token):
"""The ``{% is_author %}`` tag displays the first section, if the user is
the author of the recipe or a staff member. Otherwise the second section
is displayed.
::
{% is_author user recipe %}
The user is owner of this recipe or a staff member.
{% else %}
The user has no right to edit this recipe.
{% endis_author %}
"""
try:
tag_name, user, recipe = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
'%s requires a Recipe and an User as arguments' % token.contents.split()[0])
nodelist_true = parser.parse(('else', 'endis_author'))
token = parser.next_token()
if token.contents == 'else':
nodelist_false = parser.parse(('endis_author',))
parser.delete_first_token()
else:
nodelist_false = template.NodeList()
return IsAuthorNode(user, recipe, nodelist_true, nodelist_false)
Danach schreibst du die Node, die die Ausgabe rendert. Dieser Code muss oberhalb der Funktion do_is_author stehen, denn sonst steht die Klasse IsAuthorNode nicht in der Funktion zur Verfügung.
class IsAuthorNode(template.Node):
def __init__(self, user, recipe, nodelist_true, nodelist_false):
self.user = template.Variable(user)
self.recipe = template.Variable(recipe)
self.nodelist_true = nodelist_true
self.nodelist_false = nodelist_false
def render(self, context):
try:
user = self.user.resolve(context)
recipe = self.recipe.resolve(context)
except template.VariableDoesNotExist:
return ''
if recipe.author.id == user.id or user.is_staff:
return self.nodelist_true.render(context)
else:
return self.nodelist_false.render(context)
Nun kannst du das neue Templatetag nutzen, zum Beispiel im Template recipes/templates/recipes/detail.html.
Dazu muss zuerst unser Templatetag geladen werden. Das machst du am besten im Kopf des Templates:
{% load recipes %}
Bemerkung
Der Bezeichner hinter dem load Templatetag ist immer der Name des Python Moduls, dass die Templatetags enthält, die geladen werden sollen (ohne die Endung ”.py”). Das Python Modul muss sich im Verzeichnis templatetags einer installierten Applikation befinden.
Dann ersetzt du diese beiden Zeilen:
<a href="{% url recipes_recipe_edit object.pk %}">Rezept bearbeiten</a>
<a href="{% url recipes_recipe_index %}">zurück zur Übersicht</a>
Mit dem neuen Templatetag:
{% is_author user object %}
<a href="{% url recipes_recipe_edit object.pk %}">Rezept bearbeiten</a>
{% else %}
Bitte <a href="{% url userauth_login %}">einloggen</a>, um das Rezept zu bearbeiten.
{% endis_author %}
<a href="{% url recipes_recipe_index %}">zurück zur Übersicht</a>
Da das Schreiben von Templatetags mit Django Bordmitteln recht umständlich ist, sind verschiedene Django Apps entstanden, die dies vereinfachen. Eine Übersicht gibt das Templatetags Grid auf Django Packages. Zwei der populärsten Templatetag Apps sind django-classy-tags und django-ttag.