Buscar..


Filtros personalizados

Filtros le permite aplicar una función a una variable. Esta función puede tomar 0 o 1 argumento. Aquí está la sintaxis:

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

Los filtros se pueden encadenar por lo que esto es perfectamente válido:

{{ variable|filter_name:argument|another_filter }}

Si se traduce a python, la línea anterior daría algo como esto:

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

En este ejemplo, escribiremos un filtro personalizado verbose_name que se aplique a un Modelo (instancia o clase) o un QuerySet. Devolverá el nombre detallado de un modelo o su nombre completo en plural si el argumento se establece en 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

Etiquetas simples

La forma más sencilla de definir una etiqueta de plantilla personalizada es usar un simple_tag . Estos son muy fáciles de configurar. El nombre de la función será el nombre de la etiqueta (aunque puede anularlo), y los argumentos serán tokens ("palabras" separadas por espacios, excepto los espacios entre comillas). Incluso soporta argumentos de palabras clave.

Aquí hay una etiqueta inútil que ilustrará nuestro ejemplo:

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

Sean foo y baz variables de contexto como las siguientes:

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

Digamos que queremos que esta etiqueta muy inútil se muestre así:

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

Tipo de concatenación de argumentos repetida 3 veces (3 es el primer argumento).

Aquí está el aspecto de la implementación de la etiqueta:

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 permite marcar <br/> como HTML seguro, pero no el contenido de los outputs .

Etiquetas personalizadas avanzadas usando Nodo

A veces, lo que quieres hacer es demasiado complejo para un filter o un simple_tag . De este modo, deberá crear una función de compilación y un renderizador.

En este ejemplo, crearemos una etiqueta de plantilla verbose_name con la siguiente sintaxis:

Ejemplo Descripción
{% verbose_name obj %} Nombre detallado de un modelo
{% verbose_name obj 'status' %} Nombre detallado del campo "estado"
{% verbose_name obj plural %} Verbose nombre plural de un modelo
{% verbose_name obj plural capfirst %} Nombre verbal en mayúscula plural de un modelo
{% verbose_name obj 'foo' capfirst %} Nombre detallado en mayúscula de un campo
{% verbose_name obj field_name %} Nombre detallado de un campo de una variable
{% verbose_name obj 'foo'|add:'_bar' %} Nombre detallado de un campo "foo_bar"

La razón por la que no podemos hacer esto con una etiqueta simple es que plural y capfirst no son variables ni cadenas, son "palabras clave". Obviamente, podríamos decidir pasarlos como cadenas 'plural' y 'capfirst' , pero puede entrar en conflicto con los campos con estos nombres. ¿ {% verbose_name obj 'plural' %} significaría "nombre detallado plural de obj " o "nombre detallado de obj.plural "?

Primero vamos a crear la función de compilación:

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

Y ahora el renderizador:

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow