Ver índice de contenidos del libro

4.3. Etiquetas básicas de plantillas y filtros

Como hemos mencionado, el sistema de plantillas se distribuye con etiquetas y filtros incorporados. Las secciones que siguen proveen un resumen de la mayoría de las etiquetas y filtros.

4.3.1. Etiquetas

4.3.1.1. Las etiquetas if / else

La etiqueta {% if %} evalúa una variable, y si esta es "true" (esto es, existe, no está vacía y no es un valor booleano falso), el sistema mostrará todo lo que hay entre {% if %} y {% endif %}, por ejemplo:

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% endif %}

La etiqueta {% else %} es opcional:

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% else %}
    <p>Get back to work.</p>
{% endif %}

Nota En Python, la lista vacía ([]), la tupla vacía (()), el diccionario vacío ({}), la cadena vacía (''), el cero (0), y el objeto especial None son False en un contexto booleano. Todo lo demás es True.

La etiqueta {% if %} acepta and, or, o not para testear múltiples variables, o para negarlas. Por ejemplo:

{% if athlete_list and coach_list %}
    Both athletes and coaches are available.
{% endif %}
 
{% if not athlete_list %}
    There are no athletes.
{% endif %}
 
{% if athlete_list or coach_list %}
    There are some athletes or some coaches.
{% endif %}
 
{% if not athlete_list or coach_list %}
    There are no athletes or there are some coaches. (OK, so
    writing English translations of Boolean logic sounds
    stupid; it's not our fault.)
{% endif %}
 
{% if athlete_list and not coach_list %}
    There are some athletes and absolutely no coaches.
{% endif %}

Las etiquetas {% if %} no permiten las cláusulas and y or en la misma etiqueta, porque el orden de evaluación lógico puede ser ambiguo. Por ejemplo, esto es inválido:

{% if athlete_list and coach_list or cheerleader_list %}

No se admite el uso de paréntesis para controlar el orden de las operaciones. Si necesitas paréntesis, considera efectuar la lógica en el código de la vista para simplificar las plantillas. Aún así, si necesitas combinar and y or para hacer lógica avanzada, usa etiquetas {% if %} anidadas, por ejemplo:

{% if athlete_list %}
    {% if coach_list or cheerleader_list %}
        We have athletes, and either coaches or cheerleaders!
    {% endif %}
{% endif %}

Usar varias veces el mismo operador lógico están bien, pero no puedes combinar diferentes operadores. Por ejemplo, esto es válido:

{% if athlete_list or coach_list or parent_list or teacher_list %}

Ahí no hay una etiqueta {% elif %}. Usa etiquetas {% if %} anidadas para conseguir alguna cosa:

{% if athlete_list %}
    <p>Here are the athletes: {{ athlete_list }}.</p>
{% else %}
    <p>No athletes are available.</p>
    {% if coach_list %}
        <p>Here are the coaches: {{ coach_list }}.</p>
    {% endif %}
{% endif %}

Asegúrate de cerrar cada {% if %} con un {% endif %}. En otro caso, Django levantará la excepción TemplateSyntaxError.

4.3.1.2. La etiqueta for

La etiqueta {% for %} permite iterar sobre cada uno de los elementos de una secuencia. Como en la sentencia for de Python, la sintaxis es for X in Y, dónde Y es la secuencia sobre la que se hace el bucle y X es el nombre de la variable que se usará para cada uno de los ciclos del bucle. Cada vez que atravesamos el bucle, el sistema de plantillas renderizará todo entre {% for %} y {% endfor %}.

Por ejemplo, puedes usar lo siguiente para mostrar una lista de atletas tomadas de la variable athlete_list:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

Agrega reversed a la etiqueta para iterar sobre la lista en orden inverso:

{% for athlete in athlete_list reversed %}
...
{% endfor %}

Es posible anidar etiquetas {% for %}:

{% for country in countries %}
    <h1>{{ country.name }}</h1>
    <ul>
    {% for city in country.city_list %}
        <li>{{ city }}</li>
    {% endfor %}
    </ul>
{% endfor %}

No se admite la "ruptura" de un bucle antes de que termine. Si quieres conseguir esto, cambia la variable sobre la que estás iterando para que incluya sólo los valores sobre los cuales quieres iterar. De manera similar, no se soporta la sentencia continue que se encarga de retornar inmediatamente al inicio del bucle. (Lee la sección Filosofía y limitaciones al final de este capítulo para comprender el razonamiento detrás de este decisión de diseño.)

La etiqueta {% for %} asigna la variable forloop mágica a la plantilla con el bucle. Esta variable tiene algunos atributos que toman información acerca del progreso del bucle:

  • forloop.counter es siempre asignada a un número entero representando el número de veces que se ha entrado en el bucle. Esta es indexada a partir de 1, por lo que la primera vez que se ingresa al bucle, forloop.counter será 1. Aquí un ejemplo:
{% for item in todo_list %}
    <p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}
  • forloop.counter0 es como forloop.counter, excepto que esta es indexada a partir de cero. Contendrá el valor 0 la primera vez que se atraviese el bucle.
  • forloop.revcounter es siempre asignada a un entero que representa el número de iteraciones que faltan para terminar el bucle. La primera vez que se ejecuta el bucle forloop.revcounter será igual al número de elementos que hay en la secuencia. La última vez que se atraviese el bucle, a forloop.revcounter se la asignará el valor 1.
  • forloop.revcounter0 es como forloop.revcounter, a excepción de que está indexada a partir de cero. La primera vez que se atraviesa el bucle, forloop.revcounter0 es asignada al número de elementos que hay en la secuencia menos 1. La última vez que se atraviese el bucle, el valor de esta será 0.
  • forloop.first es un valor booleano asignado a True si es la primera vez que se pasa por el bucle. Esto es conveniente para ocasiones especiales:
{% for object in objects %}
    {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
    {{ object }}
    </li>
{% endfor %}
  • forloop.last es un valor booleano asignado a True si es la última pasada por el bucle. Un uso común es para esto es poner un carácter pipe entre una lista de enlaces:
{% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}

El código de la plantilla de arriba puede mostrar algo parecido a esto: Link1 | Link2 | Link3 | Link4

  • forloop.parentloop esta es una referencia al objeto padre de forloop, en el caso de bucles anidados. Aquí un ejemplo::
{% for country in countries %}
    <table>
    {% for city in country.city_list %}
        <tr>
        <td>Country #{{ forloop.parentloop.counter }}</td>
        <td>City #{{ forloop.counter }}</td>
        <td>{{ city }}</td>
        </tr>
    {% endfor %}
    </table>
{% endfor %}

La variable mágica forloop está sólo disponible dentro de bucles. Después de que el analizados sintáctico encuentra {% endfor %}, forloop desaparece.

Nota Dentro de un bloque {% for %}, las variables existentes se mueven fuera de tal manera de evitar sobreescribir la variable mágica forloop. Django expone ese contexto movido en forloop.parentloop. Generalmente no necesitas preocuparte por esto, si provees una variable a la plantilla llamada forloop (a pesar de que no lo recomendamos), se llamará forloop.parentloop mientras esté dentro del bloque {% for %}.

4.3.1.3. Las etiquetas ifequal / ifnotequal

El sistema de plantillas de Django a propósito no es un lenguaje de programación completo y por lo tanto no permite ejecutar sentencias arbitrarias de Python. (Más sobre esta idea en la sección Filosofía y limitaciones al final de este capítulo).

Sin embargo, es bastante común que una plantilla requiera comparar dos valores y mostrar algo si ellos son iguales — Django provee la etiqueta {% ifequal %} para este propósito.

La etiqueta {% ifequal %} compara dos valores y muestra todo lo que se encuentra entre {% ifequal %} y {% endifequal %} si el valor es igual.

Este ejemplo compara las variables user y currentuser de la plantilla:

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

Los argumentos pueden ser cadenas de texto encerradas con comillas simples o dobles. Ejemplo:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% endifequal %}
 
{% ifequal section "community" %}
    <h1>Community</h1>
{% endifequal %}

Como {% if %}, la etiqueta {% ifequal %} admite un opcional {% else %}:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

Sólo las variables de la plantilla, cadenas, enteros y números decimales son permitidos como argumentos para {% ifequal %}. Estos son ejemplos válidos:

{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}

Cualquier otro tipo de variables, tales como diccionarios de Python, listas, o booleanos, no pueden ser comparados en {% ifequal %}. Estos ejemplos son inválidos:

{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}

Si necesitas comprobar cuando algo es verdadero o falso, usa la etiqueta {% if %} en vez de {% ifequal %}.

4.3.1.4. Comentarios

Al igual que en HTML o en un lenguaje de programación como Python, el lenguaje de plantillas de Django permite comentarios. Para designar un comentario, usa {# #}:

{# Esto es un comentario #}

Este comentario no será mostrado cuando la plantilla sea renderizada.

Un comentario no puede abarcar múltiples líneas. Esta limitación mejora el rendimiento del analizador sintáctico de plantillas. En la siguiente plantilla, la salida del renderizado mostraría exactamente lo mismo que la plantilla (esto es, la etiqueta comentario no será tomada como comentario):

This is a {# this is not
a comment #}
test.

4.3.2. Filtros

Como explicamos anteriormente en este capítulo, los filtros de plantillas son formas simples de alterar el valor de una variable antes de mostrarla. Los filtros se parecen a esto:

{{ name|lower }}

Esto muestra el valor de {{ name }} después de aplicarle el filtro lower, el cual convierte el texto a minúscula. Usa un pipe (|) para aplicar el filtro.

Los filtros pueden estar en cadena — eso es, la salida del uno de los filtros puede ser aplicada al próximo. Aquí un modismo común para escapar contenido del texto, y entonces convertir los saltos de líneas en etiquetas <p>:

{{ my_text|escape|linebreaks }}

Algunos filtros toman argumentos. Un filtro con argumento se ve de este modo:

{{ bio|truncatewords:"30" }}

Esto muestra las primeras 30 palabras de la variable bio. Los argumentos de los filtros están siempre entre comillas dobles.

Los siguientes son algunos de los filtros más importantes; el Apéndice F cubre el resto.

  • addslashes: Agrega una con contra-barra antes de cualquier contra-barra, comilla simple o comilla doble. Esto es útil si el texto producido está incluido en un string de JavaScript.
  • date: Formatea un objeto date o datetime de acuerdo al formato tomado como parámetro (el formato de los strings está definido en el Apéndice F). Ejemplo:
{{ pub_date|date:"F j, Y" }}
  • escape: Escapa ampersands (&), comillas, y corchetes del string tomado. Esto es usado para desinfectar datos suministrados por el usuario y asegurar que los datos son válidos para XML y XHTML. Específicamente, escape hace estas conversiones:

    • Convierte & en &amp;
    • Convierte < en &lt;
    • Convierte > en &gt;
    • Convierte " (comilla doble) en &quot;
    • Convierte ' (comilla simple) en &#39;
  • length: Retorna la longitud del valor. Puedes usar este con una lista o con un string, o con cualquier objeto Python que sepa como determinar su longitud (o sea cualquier objeto que tenga el método __len__()).