Recherche…


Filtres personnalisés

Les filtres vous permettent d'appliquer une fonction à une variable. Cette fonction peut prendre 0 ou 1 argument. Voici la syntaxe:

{{ variable|filter_name }} 
{{ variable|filter_name:argument }}

Les filtres peuvent être chaînés, ce qui est parfaitement valable:

{{ variable|filter_name:argument|another_filter }}

Si traduit en python la ligne ci-dessus donnerait quelque chose comme ceci:

print(another_filter(filter_name(variable, argument)))

Dans cet exemple, nous allons écrire un filtre personnalisé verbose_name qui s'applique à un modèle (instance ou classe) ou à un QuerySet. Il renverra le nom verbeux d'un modèle ou son nom verbeux si l'argument est défini sur 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

Tags simples

La manière la plus simple de définir une balise de modèle personnalisée consiste à utiliser une simple_tag . Celles-ci sont très simples à configurer. Le nom de la fonction sera le nom de la balise (bien que vous puissiez la remplacer), et les arguments seront des jetons ("mots" séparés par des espaces, sauf les espaces entre guillemets). Il prend même en charge les arguments de mots clés.

Voici un tag inutile qui illustrera notre exemple:

{% useless 3 foo 'hello world' foo=True bar=baz.hello|capfirst %}

Laissez foo et baz être des variables de contexte comme suit:

{'foo': "HELLO", 'baz': {'hello': "world"}}

Disons que nous voulons que cette balise très inutile se présente comme ceci:

HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>

Type d’argument concaténation répétée 3 fois (3 étant le premier argument).

Voici à quoi peut ressembler l'implémentation du tag:

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 permet de marquer <br/> comme HTML sécurisé, mais pas le contenu des outputs .

Balises personnalisées avancées utilisant Node

Parfois, ce que vous voulez faire est trop complexe pour un filter ou un simple_tag . Pour ce faire, vous devrez créer une fonction de compilation et un rendu.

Dans cet exemple, nous allons créer une balise template verbose_name avec la syntaxe suivante:

Exemple La description
{% verbose_name obj %} Nom verbeux d'un modèle
{% verbose_name obj 'status' %} Nom verbeux du champ "statut"
{% verbose_name obj plural %} Nom verbeux du modèle d'un modèle
{% verbose_name obj plural capfirst %} Capitalisation nominatif pluriel d'un modèle
{% verbose_name obj 'foo' capfirst %} Nom verbeux en majuscule d'un champ
{% verbose_name obj field_name %} Nom verbeux d'un champ à partir d'une variable
{% verbose_name obj 'foo'|add:'_bar' %} Nom verbeux d'un champ "foo_bar"

La raison pour laquelle nous ne pouvons pas faire cela avec une simple balise est que plural et capfirst ne sont ni des variables ni des chaînes, ce sont des "mots-clés". Nous pourrions évidemment décider de les passer comme des chaînes 'plural' et 'capfirst' , mais cela pourrait entrer en conflit avec des champs portant ces noms. Est-ce que {% verbose_name obj 'plural' %} signifie "nom verbeux pluriel d' obj " ou "nom verbeux d' obj.plural "?

Commençons par créer la fonction de compilation:

@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)

Et maintenant le moteur de rendu:

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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow