Jinja 2 en Flask 3

- Andrés Cruz

In english

Uno de los problemas que tenemos actualmente es que, no tenemos una forma efectiva de presentar los datos al usuario final, al ser Flask una tecnología web, lo usual es presentar estos datos en una página web; es aquí donde cobra importancia lo que se conoce como motor de plantillas; un motor de plantillas es un recurso utilizado para presentar estos datos desde el servidor y generar una página web en el proceso, son la siguiente capa del MVC que corresponde a la vista y permite separar la lógica de presentación de los datos; para ello, se emplean un conjunto de características para lograr manipular los datos del servidor y generar la página HTML que será consumida mediante el usuario, características como impresiones, filtros, condicionales, ciclos o importaciones, permite generar este código HTML necesario para representar una página web. Son muy empleados en la programación del lado del servidor, ya que permiten la presentación de datos sin tener que modificar el código fuente.

En pocas palabras, con un motor de plantillas es posible presentar los datos manejados por el servidor, específicamente en nuestro caso en Flask y generar el HTML en el proceso; por ejemplo, si tenemos una lista de usuarios, podemos imprimirlo en una tabla o similar en HTML usando ciertas funcionalidades (directivas).

Sobre Jinja

Jinja es un motor de plantillas escrito en Python diseñado para ser simple y expresivo; Jinja es un motor plantillas que puede ser usado en otros frameworks como FastAPI,también es muy similar al empleado por Django.

Con Jinja como motor de plantilla podemos representar fácilmente las respuestas de la API.

El motor de plantillas de Jinja utiliza corchetes { } para distinguir sus expresiones y sintaxis del HTML:

  1. La sintaxis {{ }} se denomina bloque variable y la podemos usar para imprimir en la plantilla. 
  2. La sintaxis {% %} alberga estructuras de control como if/else, bucles y macros.
  3. La sintaxis {#  #} son para comentarios.

Configurar Jinja en Flask

De forma predeterminada, Flask busca las plantillas/templates carpeta llamada templates en el nivel raíz de la aplicación. Si esta carpeta está presente, Flask leerá automáticamente el contenido haciendo que el contenido de esta carpeta esté disponible para su uso con el método render_template() desde los controladores; así que, crearemos la carpeta mencionada anteriormente:

app\my_app\templates

Comencemos creando una página HTML básica, que sería un template en Jinja:

my_app\templates\index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Page</title>
</head>

<body>
    <h1>Hello Flask</h1>
    <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Reiciendis libero possimus nihil ipsam quam deleniti
        voluptas eaque qui. Necessitatibus corporis quas odio tenetur quos, recusandae itaque doloremque assumenda
        consequatur aliquam.</p>
</body>

</html>

Desde un controlador, devolvemos el template en vez de un texto como lo teníamos anteriormente:

my_app\__init__.py

from flask import render_template, request 

@app.route('/') 
@app.route('/hello') 
def hello_world(): 
    return render_template('index.html') 

Hagamos unos cambios puntuales para que podamos entender el potencial de Jinja en conjunto de frameworks del lado del servidor como Flask, que es pasar datos de manera dinámica desde Flask para luego consumir e imprimir desde la vista; para ello:

my_app\__init__.py

from flask import render_template, request 

@app.route('/') 
@app.route('/hello') 
def hello_world(): 
    name = request.args.get('name', 'DesarrolloLibre') 
    return render_template('index.html', name=name)

Con:

request.args.get('name', 'DesarrolloLibre') 

Se obtiene un parámetro vía GET llamado "name"; el parámetro luego es suministrado a la plantilla mediante:

name=name

En donde:

name=name

Es la variable llamada name:

name=name

Indica la forma de acceso de la variable name en el controlador desde la vista:

name=name

Desde el template o vista, imprimimos referenciando la variable desde la vista con dobles llaves:

my_app\templates\index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Page</title>
</head>

<body>
    <h1>Hello {{name}}</h1>
</body>
</html>

Y veremos en la pantalla:

http://127.0.0.1:5000/

Hello DesarrolloLibre

Puedes personalizar el nombre, pasando un parámetro vía GET:

http://127.0.0.1:5000/?name=Andrew

Hello Andrew

En el caso de que quieras personalizar el nombre de la carpeta o ubicación de los templates, lo puedes definir al momento de crear la instancia principal de la aplicación:

app = Flask(__name__, template_folder="<ROUTETEMPLATES>")

Las distintas características que nos ofrece Jinja como la anterior, las veremos en detalle en el siguiente apartado.

Primeros pasos con Jinja

En este apartado vamos a conocer las características principales de Jinja, que van desde el uso de bloques de control, hasta el uso de filtros y del template en general.

Bloques de control

Uno de los elementos cruciales en los motores de plantillas son los bloques de control; podemos realizar desde condicionales hasta ciclos for que funcionan de la misma manera que en Python; con esto es posible realizar lógicas condicionales para renderizar bloques HTML si se cumple una condición al igual que iterar colecciones de datos completas, por ejemplo, un listado de tareas en una tabla o similares.

Condicionales

Podemos hacer uso de los condicionales para evaluar condiciones de verdadero y falso:

<div>
    {% if True %}
        Is TRUE
    {% endif %}
</div>

Los condicionales en Jinja, son exactamente iguales que en Python, podemos usar el else, or, and, etc:

<div>
    {% if True and False %}
        Is True
    {% else %}
        Not is True
    {% endif %}
</div>

Ciclo for

Al igual que ocurre con el ciclo for en Python, podemos usarlo en Jinja:

<ul>
{% for item in items %}
    <li>{{ item }}</li>
{% endfor %}
</ul>

Dentro de un bloque de bucle for, puede acceder a algunas variables especiales, entre las principales tenemos:

  • loop.index: Obtiene el índice actual de la iteración comenzando desde uno.
  • loop.index0: Obtiene el índice actual de la iteración comenzando desde cero.
  • loop.first: True si es la primera iteración.
  • loop.last: True si es la última iteración.
  • loop.length: Retorna el tamaño de la lista.

Por ejemplo, para usar la variable de last:

<ul>
    {% for e in [1,2,3,4,5,6,10] %}
    <li>{{ e }} {{ loop.last }} </li>
    {% endfor %}
</ul>

Todas las variables deben de ser empleadas dentro del ciclo for.

Filtros

Los filtros en Jinja son una de las características más importantes y versátiles que existen en este motor de plantillas; permiten manipular el contenido que se muestra en una plantilla. Hay varios filtros predefinidos en Jinja aunque podemos extender los existentes con filtros personalizados; los filtros se aplican a las variables que separa por ejemplo contar la longitud de una cadena de texto, unir textos en uno solo, separar por algún separador, dar formatos a fechas números y un largo etc; en resumen, permiten realizar manipulaciones personalizadas del contenido.

Para emplear un filtro, se usa el operador de pipe (|) luego de especificar el argumento o argumentos, y luego del operador de pipe, el filtro que quieras emplear:

<DATA> | <FILTER>

Veamos algunos filtros en Jinja.

Filtro default

El filtro default se usa para reemplazar la salida si la variable tiene un valor nulo (no se encuentra definida):

{{ task | default('This is a default Task') }}

Por ejemplo, si task es nula, tendremos una salida como:

This is a default Task

Si task estuviera definida o evaluamos, por ejemplo, un número.

{{ 5 | default('This is a default Task') }}

Veríamos el número por pantalla:

5

Filtro escape

Reemplace los caracteres &, <, >, ' y " en el string suministrado con caracteres seguros para mostrar el texto que contenga los caracteres HTML:

{{ "<title>Todo Application</title>" | escape }}

Tendremos como salida:

&lt;title1&gt;Todo Application&lt;/title1&gt;

Filtro conversion

Estos filtros incluyen filtros int y float utilizados para convertir de un tipo de datos a otro:

{{ 3.142 | int }}

Tendremos como salida:

3

O por ejemplo:

{{ 31 | float }}

Tendremos como salida:

31.0

Filtro max

Devuelve el elemento más grande en la lista:

{{ [1, 2, 3, 4, 5]|max }}

Y tendremos:

5

Filtro min

Devuelve el elemento más pequeño en la lista:

{{ [1, 2, 3, 4, 5]|min }}

Y tendremos:

1

Bloques

Muchas de las funcionalidades que podemos usar en Jinja residen en bloques de código. Estos lucen como:

{% <BLOCKTYPE> %} 
<BODY>
{% <ENDBLOCKTYPE> %}

Los bloques de código permiten tanto una mejor ordenación para el código como dividir el bloque en secciones usando diversos tipos de bloques que permiten realizar una determinada función.

Herencia de templates

En este apartado, veremos cómo usar los templates en Jinja; específicamente como poder usar templates maestros para poder reutilizar un esqueleto base en múltiples vistas y cómo incluir fragmentos de vistas (otras plantillas) en plantillas.

En este apartado usaremos el término vista, template y plantilla de manera equivalente.

Template maestro

Muchas veces es necesario tener una vista maestra en donde se actualice cierto contenido clave; por ejemplo, si tenemos un módulo de gestión, los elementos comunes entre pantalla pudieran ser una cabecera para definir los enlaces, así como logo, el footer y un contenedor, los elementos dinámicos se presentaron en el medio de la pantalla.

En Jinja podemos usar templates maestros para implementar este tipo de lógicas; a la final, una vista maestra no es más que un template, en donde tenemos bloques o apartados especiales (block) para indicar en donde va el contenido que va a variar y este template maestro es incluido en los templates finales y mediante los bloques especificado en el template maestro, se referencian en los templates finales con el contenido final; esta funcionalidad de Jinja se conoce como herencia de plantillas.

Creemos una plantilla base o esqueleto que es la que va a ser usada para ser heredada desde otras plantillas:

master.html

<!DOCTYPE html>
<html lang="es">
<head>
    {% block head %}
        <link rel="stylesheet" href="style.css" />
        <title>{% block title %}{% endblock %} - DesarrolloLibre</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
           DL by <a href="#">Contact</a>.
        {% endblock %}
    </div>
</body>
</html>

En el código anterior, se definen 3 bloques:

  1. Uno para el título en el header.
  2. Uno para el título en el contenido.
  3. Otro para el contenido, que serían el lugar donde colocamos el listado, formulario entre otros.
Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy