Dungeon of Bits
Aprendiendo informática.
Dungeon of Bits
Utilizar templates con Python y Jinja2

En este tutorial vamos a utilizar Jinja2 para formatear datos desde Python usando plantillas.

Jinja2

Requisitos:

Para realizar esta práctica tan solo necesitamos un sistema con Python instalado. En nuestro caso vamos a utilizar un equipo con Ubuntu 22.04 LTS y Pycharm como framework de trabajo.

Para instalar Pycharm desde Ubuntu tan solo necesitamos teclear la instrucción:

sudo snap install pycharm-community --classic

También necesitaremos la librería Jinja2 de Python, la cual instalaremos en nuestro Virtual Environment de Python con:

sudo pip install jinja2

Recordad hacerlo en vuestro entorno virtual, el cual activaréis en vuestro directorio de proyecto:

cd directorioDeProyecto
sudo . venv/bin/activate

Importación de la librería Jinja2:

Nuestro programa utilizará la librería o módulo Jinja2 que hemos instalado, para ello al principio del programa escribiremos:

from jinja2 import Environment, FileSystemLoader

Directorio con las plantillas (templates):

Nuestro proyecto tendrá un directorio donde guardaremos las plantillas o templates, el cual se llamará templates.

Este directorio será donde guardaremos los ficheros de plantillas ya que indicaremos a Jinja que las plantillas estarán en él.

En nuestro proyecto indicaremos que las plantillas están en el directorio templates e indicaremos que usaremos la plantilla llamada "plantilla1.html" que crearemos en el directorio templates.

environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("plantilla1.html")

La plantilla:

El template o plantilla a utilizar es un fichero de texto que contiene bloques texto que se substituirán por la información que le pasemos.

Los ficheros de plantilla son ficheros de texto que pueden tener extensión: html, htm, xml...

Por ejemplo la plantilla plantilla1.html contiene este texto en formato HTML.

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>
        {{ titulo }}
    </title>
</head>
<body>
    <h1>Hola {{ nombre }}!</h1>
    <p>Me alegro de verte por aquí Señor <b>{{ apellido }}</b>. </p>
</body>
</html>

Hay tres bloques de texto los cuales se sustituirán por los valores de las variables titulo, nombre y apellido y tienen esta estructura:

{{ nombreVariable }}

La información que se pasa a un template:

Los datos que pasamos a una plantilla para que se vaya creando vienen en forma de diccionario o de pares clave, valor.

Para el ejemplo pasaremos los datos en forma de diccionario:

info = {"titulo":"La web de El Señor de los Anillos.","nombre":"John","apellido":"Tolkien"}

Pasar la información a la plantilla con el método render:

El método render pasará la información que tenemos en la variable info a nuestra plantilla y realizará las substituciones necesarias:

contenido = template.render(info)

Escribir el fichero html:

Guardamos el texto final en un fichero de texto que será una web en la que las variables habrán sido substituidas por los valores que les hemos pasado:

file = open("webFinal.html","w")
file.write(contenido)

El resultado final al abrir la web webFinal.html es:

image

El código del programa entero es:

from jinja2 import Environment, FileSystemLoader

environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("plantilla1.html")

info = {"titulo":"La web de El Señor de los Anillos.","nombre":"John","apellido":"Tolkien"}

contenido = template.render(info)

file = open("WebFinal.html","w")
file.write(contenido)

Información que se pasa a la plantilla:

En principio a la plantilla, o más bien al método render de la misma, siempre se pasará un objeto YAML, JSON o un diccionario de Python.

En los ejemplos veremos como pasar diccionarios con los datos a las plantillas.

Bucles for:

Las plantillas nos permiten realizar bucles con el siguiente código:

{% for elemento in diccionario %}
{% endfor %}

Este bucle generará todo el código que haya entre for y endfor por cada elemento que le pasemos a la plantilla.

Veamos un ejemplo en el que tenemos 4 alumnos con su nota en una asignatura:

from jinja2 import Environment, FileSystemLoader

environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("alumnos.html")

alumnos = {"alumnos":[
    {'codigo':1,'nombre':'Ford','apellido':'Fairlane','nota':5},
    {"codigo":2,"nombre":"Pierre","apellido":"Nodoyuna","nota":1},
    {"codigo":3,"nombre":"Mario","apellido":"Mario","nota":3},
    {"codigo":4,"nombre":"Jill","apellido":"Valentine","nota": 7}
]}

file = open("notas.html","w")
textoFinal = template.render(alumnos)
file.write(textoFinal)

Este será el código base que usaremos para pasar la información a la plantilla alumnos.html.

Dentro del fichero plantilla crearemos un bucle que nos devolverá la información de cada alumno:

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <title>Listado de notas </title>
</head>

<body>
<h1>Listado de notas:</h1>
<ul>
{% for alumno in alumnos %}
  <li>
  {{ alumno.get("codigo") }} - {{ alumno.get("apellido") }},{{ alumno.get("nombre") }}: {{ alumno.get("nota") }}
  </li>
{% endfor %}
</ul>

</body>
</html>

Pero veamos la parte del bucle:

{% for alumno in alumnos %}
  <li>
  {{ alumno.get("codigo") }} - {{ alumno.get("apellido") }},{{ alumno.get("nombre") }}: {{ alumno.get("nota") }}
  </li>
{% endfor %}

Este bucle muestra el código, el apellido, el nombre y, por último, la nota de cada alumno/a que se le pasa. Al ejecutarlo se genera un fichero llamado notas.html que queda así:

image

Condicional if:

Vamos a mejorar un poco la plantilla añadiendo CSS inline que ponga de color verde los alumnos/as aprobados y de color rojo los suspendidos/as.

solo modificaremos la plantilla con un bloque de código if.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <title>Listado de notas </title>
</head>

<body>
<h1>Listado de notas:</h1>
<ul>
{% for alumno in alumnos %}
  {% if alumno.get("nota")<5 %}
    <li style="color:red;">
  {% else %}
    <li style="color:green;">
  {% endif %}
  {{ alumno.get("codigo") }} - {{ alumno.get("apellido") }},{{ alumno.get("nombre") }}: {{ alumno.get("nota") }}
  </li>
{% endfor %}
</ul>

</body>
</html>

El resultado es el siguiente:

image

Herencia en plantillas:

Como hemos visto para realizar una página web sencilla usamos poco código html, pero... ¿Qué pasa si intentamos crear una web real con plantillas y hemos de renderizar toda la web a la vez?

Pues que tendremos unos ficheros poco manejables por extensión.

Por ello las plantillas permiten la herencia, eso es así con la plaabra reservada extends y con los bloques de código.

Imagina que tienes una plantilla en la que quieres simplemente mostrar unos datos de una página web pero sin modificar toda la estructura de la misma ni tener que copiar todo el html cada vez a una plantilla nueva.

Vamos a tomar el ejemplo anterior del listado de notas de alumnos, en el tenemos una plantilla que será la plantilla de base, la que tiene el código de nuestra web, esa plantilla la llamaremos base.html.

<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <title>Plantilla base</title>
</head>

<body>
{% block content %}

{% endblock %}

</body>
</html>

En esta plantilla hemos definido un bloque de código que se llama content, el cual será substituido por otra plantilla más adelante.

y ahora crearemos una plantilla llamada content.html la cual tendrá, solamente, el código HTML para el listado de alumnos anterior.

{% extends "base.html" %}
{% block content %}

<h1>Listado de notas:</h1>
<ul>
{% for alumno in alumnos -%}
  {% if alumno.get("nota")<5 -%}
    <li style="color:red;">
  {% else -%}
    <li style="color:green;">
  {% endif -%}
  {{ alumno.get("codigo") }} - {{ alumno.get("apellido") }},{{ alumno.get("nombre") }}: {{ alumno.get("nota") }}
  </li>
{% endfor %}
</ul>

{% endblock %}

Como se ve la plantilla content extiende la plantilla base.html o, lo que es lo mismo, busca los bloques que pueden ser substituidos en la plantilla base, que en nuestro ejemplo es el bloque content.

Ahora usaremos el mismo programa en Python, pero con la plantilla conten.html, la cual usará, a su vez, la plantilla base.html.

from jinja2 import Environment, FileSystemLoader

environment = Environment(loader=FileSystemLoader("templates/"))
template = environment.get_template("content.html")

alumnos = {"alumnos":[
    {'codigo':1,'nombre':'Ford','apellido':'Fairlane','nota':5},
    {"codigo":2,"nombre":"Pierre","apellido":"Nodoyuna","nota":1},
    {"codigo":3,"nombre":"Mario","apellido":"Mario","nota":3},
    {"codigo":4,"nombre":"Jill","apellido":"Valentine","nota": 7}
]}

file = open("notas.html","w")
textoFinal = template.render(alumnos)
file.write(textoFinal)

El resultado final será el mismo que antes:

image