Django
Vorlagen-Tags und Filter
Suche…
Benutzerdefinierte Filter
Mit Filtern können Sie eine Funktion auf eine Variable anwenden. Diese Funktion kann 0 oder 1 Argument annehmen. Hier ist die Syntax:
{{ variable|filter_name }}
{{ variable|filter_name:argument }}
Filter können verkettet werden, so dass dies vollkommen gültig ist:
{{ variable|filter_name:argument|another_filter }}
Wenn in Python übersetzt, würde die obige Zeile ungefähr Folgendes ergeben:
print(another_filter(filter_name(variable, argument)))
In diesem Beispiel schreiben wir einen benutzerdefinierten Filter verbose_name
, der für ein Modell (Instanz oder Klasse) oder ein QuerySet gilt. Es wird den ausführlichen Namen eines Modells oder dessen ausführlichen Namen Plural zurückgegeben, wenn das Argument auf True
.
@register.filter
def verbose_name(model, plural=False):
"""Return the verbose name of a model.
`model` can be either:
- a Model class
- a Model instance
- a QuerySet
- any object refering to a model through a `model` attribute.
Usage:
- Get the verbose name of an object
{{ object|verbose_name }}
- Get the plural verbose name of an object from a QuerySet
{{ objects_list|verbose_name:True }}
"""
if not hasattr(model, '_meta'):
# handle the case of a QuerySet (among others)
model = model.model
opts = model._meta
if plural:
return opts.verbose_name_plural
else:
return opts.verbose_name
Einfache Tags
Die einfachste Methode zum Definieren eines benutzerdefinierten Vorlagen-Tags ist die Verwendung eines simple_tag
. Diese sind sehr einfach einzurichten. Der Funktionsname ist der Tag-Name (obwohl Sie ihn überschreiben können), und Argumente sind Token ("Wörter", die durch Leerzeichen getrennt sind, mit Ausnahme von Leerzeichen zwischen Anführungszeichen). Es unterstützt sogar Keyword-Argumente.
Hier ist ein nutzloser Tag, der unser Beispiel veranschaulicht:
{% useless 3 foo 'hello world' foo=True bar=baz.hello|capfirst %}
Sei foo
und baz
Kontextvariablen wie die folgenden:
{'foo': "HELLO", 'baz': {'hello': "world"}}
Angenommen, wir möchten, dass dieses sehr nutzlose Tag so dargestellt wird:
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
Art der Verkettung der Argumente dreimal wiederholt (3 ist das erste Argument).
So kann die Tag-Implementierung aussehen:
from django.utils.html import format_html_join
@register.simple_tag
def useless(repeat, *args, **kwargs):
output = ';'.join(args + ['{}:{}'.format(*item) for item in kwargs.items()])
outputs = [output] * repeat
return format_html_join('\n', '{}<br/>', ((e,) for e in outputs))
format_html_join
erlaubt es, <br/>
als sicheres HTML zu kennzeichnen, nicht jedoch den Inhalt der outputs
.
Erweiterte benutzerdefinierte Tags mit Node
Manchmal ist das, was Sie tun möchten, für einen filter
oder einen simple_tag
zu komplex. Um dies zu erreichen, müssen Sie eine Kompilierungsfunktion und einen Renderer erstellen.
In diesem Beispiel erstellen wir ein Vorlagen-Tag " verbose_name
mit der folgenden Syntax:
Beispiel | Beschreibung |
---|---|
{% verbose_name obj %} | Ausführlicher Name eines Modells |
{% verbose_name obj 'status' %} | Ausführlicher Name des Feldes "Status" |
{% verbose_name obj plural %} | Ausführlicher Name Plural eines Modells |
{% verbose_name obj plural capfirst %} | Großbuchstabe verbose Name Plural eines Modells |
{% verbose_name obj 'foo' capfirst %} | Großbuchstabe verbose Name eines Feldes |
{% verbose_name obj field_name %} | Ausführlicher Name eines Feldes aus einer Variablen |
{% verbose_name obj 'foo'|add:'_bar' %} | Ausführlicher Name eines Feldes "foo_bar" |
Der Grund, warum wir dies mit einem einfachen Tag nicht tun können, ist, dass plural
und capfirst
weder Variablen noch Zeichenfolgen sind, sondern "Schlüsselwörter". Wir könnten natürlich entscheiden, sie als Strings 'plural'
und 'capfirst'
, aber es kann Konflikte mit Feldern mit diesen Namen geben. Würde {% verbose_name obj 'plural' %}
"verbose name plural von obj
" oder "verbose name von obj.plural
" obj.plural
?
Zuerst erstellen wir die Kompilierungsfunktion:
@register.tag(name='verbose_name')
def do_verbose_name(parser, token):
"""
- parser: the Parser object. We will use it to parse tokens into
nodes such as variables, strings, ...
- token: the Token object. We will use it to iterate each token
of the template tag.
"""
# Split tokens within spaces (except spaces inside quotes)
tokens = token.split_contents()
tag_name = tokens[0]
try:
# each token is a string so we need to parse it to get the actual
# variable instead of the variable name as a string.
model = parser.compile_filter(tokens[1])
except IndexError:
raise TemplateSyntaxError(
"'{}' tag requires at least 1 argument.".format(tag_name))
field_name = None
flags = {
'plural': False,
'capfirst': False,
}
bits = tokens[2:]
for bit in bits:
if bit in flags.keys():
# here we don't need `parser.compile_filter` because we expect
# 'plural' and 'capfirst' flags to be actual strings.
if flags[bit]:
raise TemplateSyntaxError(
"'{}' tag only accept one occurrence of '{}' flag".format(
tag_name, bit)
)
flags[bit] = True
continue
if field_name:
raise TemplateSyntaxError((
"'{}' tag only accept one field name at most. {} is the second "
"field name encountered."
).format(tag_name, bit)
field_name = parser.compile_filter(bit)
# VerboseNameNode is our renderer which code is given right below
return VerboseNameNode(model, field_name, **flags)
Und jetzt der Renderer:
class VerboseNameNode(Node):
def __init__(self, model, field_name=None, **flags):
self.model = model
self.field_name = field_name
self.plural = flags.get('plural', False)
self.capfirst = flags.get('capfirst', False)
def get_field_verbose_name(self):
if self.plural:
raise ValueError("Plural is not supported for fields verbose name.")
return self.model._meta.get_field(self.field_name).verbose_name
def get_model_verbose_name(self):
if self.plural:
return self.model._meta.verbose_name_plural
else:
return self.model._meta.verbose_name
def render(self, context):
"""This is the main function, it will be called to render the tag.
As you can see it takes context, but we don't need it here.
For instance, an advanced version of this template tag could look for an
`object` or `object_list` in the context if `self.model` is not provided.
"""
if self.field_name:
verbose_name = self.get_field_verbose_name()
else:
verbose_name = self.get_model_verbose_name()
if self.capfirst:
verbose_name = verbose_name.capitalize()
return verbose_name