Django
Sjabloontags en filters
Zoeken…
Aangepaste filters
Met filters kunt u een functie op een variabele toepassen. Voor deze functie kan 0 of 1 argument nodig zijn. Hier is de syntaxis:
{{ variable|filter_name }}
{{ variable|filter_name:argument }}
Filters kunnen worden gekoppeld, dus dit is volkomen geldig:
{{ variable|filter_name:argument|another_filter }}
Indien vertaald naar python zou de bovenstaande regel zoiets geven:
print(another_filter(filter_name(variable, argument)))
In dit voorbeeld zullen we een aangepaste filter verbose_name
die van toepassing is op een Model (instantie of klasse) of een QuerySet. Het retourneert de uitgebreide naam van een model, of de uitgebreide naam meervoud als het argument is ingesteld op 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
Eenvoudige tags
De eenvoudigste manier om een aangepaste sjabloontag te definiëren, is door een simple_tag
te gebruiken. Deze zijn zeer eenvoudig in te stellen. De functienaam is de tagnaam (hoewel u deze kunt overschrijven) en argumenten zijn tokens ("woorden" gescheiden door spaties, behalve spaties tussen aanhalingstekens). Het ondersteunt zelfs trefwoordargumenten.
Hier is een nutteloze tag die ons voorbeeld zal illustreren:
{% useless 3 foo 'hello world' foo=True bar=baz.hello|capfirst %}
Laat foo
en baz
contextvariabelen zijn zoals de volgende:
{'foo': "HELLO", 'baz': {'hello': "world"}}
Stel dat we deze zeer nutteloze tag als volgt willen weergeven:
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
Soort argumenten samenvoeging driemaal herhaald (waarvan 3 het eerste argument is).
Dit is hoe de tag-implementatie eruit zou kunnen zien:
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
maakt het mogelijk <br/>
als veilige HTML te markeren, maar niet de inhoud van de outputs
.
Geavanceerde aangepaste tags met behulp van Node
Soms is wat je wilt doen gewoon te complex voor een filter
of een simple_tag
. Dit moet je een compilatiefunctie en een renderer maken.
In dit voorbeeld maken we een sjabloontag verbose_name
met de volgende syntaxis:
Voorbeeld | Beschrijving |
---|---|
{% verbose_name obj %} | Uitgebreide naam van een model |
{% verbose_name obj 'status' %} | Uitgebreide naam van het veld "status" |
{% verbose_name obj plural %} | Uitgebreide naam meervoud van een model |
{% verbose_name obj plural capfirst %} | Hoofdletterwoord in meervoud van een model |
{% verbose_name obj 'foo' capfirst %} | Hoofdletterwoord in hoofdletters van een veld |
{% verbose_name obj field_name %} | Uitgebreide naam van een veld uit een variabele |
{% verbose_name obj 'foo'|add:'_bar' %} | Uitgebreide naam van een veld "foo_bar" |
De reden waarom we dit niet kunnen doen met een eenvoudige tag is dat plural
en capfirst
geen variabelen of strings zijn, ze zijn "sleutelwoorden". We kunnen natuurlijk besluiten om ze door te geven als 'plural'
en 'capfirst'
, maar het kan conflicteren met velden met deze namen. Zou {% verbose_name obj 'plural' %}
betekenen "uitgebreide naam meervoud van obj
" of "uitgebreide naam van obj.plural
"?
Laten we eerst de compilatiefunctie maken:
@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)
En nu de 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