Plantillas con Django Template Language

Intermedio
Django
Django
Actualizado: 27/02/2025

Creación y uso de templates desde controladores

En Django, los templates son esenciales para separar la lógica de negocio de la presentación, permitiendo construir aplicaciones web mantenibles y escalables. Para utilizar templates desde los controladores (vistas), es necesario seguir una serie de pasos que integran las plantillas en el flujo de la aplicación.

Primero, es fundamental crear un directorio llamado templates dentro de cada aplicación o en una ubicación general del proyecto. Dentro de este directorio, se pueden organizar las plantillas según las necesidades del proyecto:

mi_proyecto/
├── mi_app/
    ├── templates/
        ├── mi_app/
            ├── base.html
            ├── inicio.html
            ├── detalle.html

La estructura anterior asegura que Django pueda localizar las plantillas correctamente, especialmente cuando se tienen múltiples aplicaciones.

En el archivo settings.py, la configuración predeterminada de TEMPLATES suele ser suficiente, ya que el parámetro APP_DIRS está establecido en True, permitiendo que Django busque automáticamente en los directorios templates de cada aplicación:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],  # Directorios adicionales si es necesario
        'APP_DIRS': True,
        'OPTIONS': {
            # Opciones adicionales si se requieren
        },
    },
]

Desde los controladores, se utiliza la función render() para combinar una plantilla con un contexto y generar una respuesta HTTP. Un ejemplo sencillo de una vista podría ser:

from django.shortcuts import render

def vista_inicio(request):
    contexto = {'titulo': 'Página de Inicio'}
    return render(request, 'mi_app/inicio.html', contexto)

En este caso, la vista vista_inicio renderiza la plantilla inicio.html ubicada en mi_app/templates/mi_app/, y le pasa un contexto con datos que pueden ser utilizados dentro de la plantilla.

La plantilla inicio.html podría tener el siguiente contenido:

<!DOCTYPE html>
<html>
<head>
    <title>{{ titulo }}</title>
</head>
<body>
    <h1>Bienvenido a Mi Aplicación</h1>
    <p>Esta es la página de inicio.</p>
</body>
</html>

Dentro de la plantilla, se utilizan las variables de contexto mediante la sintaxis {{ variable }} proporcionada por el Django Template Language (DTL). Esto permite insertar contenido dinámico en el HTML.

Para manejar datos más complejos, como listas u objetos, se pueden pasar al contexto y utilizarlos en la plantilla. Por ejemplo:

def vista_productos(request):
    lista_productos = Producto.objects.all()
    contexto = {'productos': lista_productos}
    return render(request, 'mi_app/productos.html', contexto)

Y en la plantilla productos.html:

<!DOCTYPE html>
<html>
<head>
    <title>Listado de Productos</title>
</head>
<body>
    <h1>Productos Disponibles</h1>
    <ul>
        {% for producto in productos %}
            <li>{{ producto.nombre }} - {{ producto.precio }}€</li>
        {% endfor %}
    </ul>
</body>
</html>

Aquí se utiliza una estructura iterativa con {% for %} para recorrer la lista de productos y mostrarlos en un formato legible.

Es posible también utilizar condicionales en las plantillas para mostrar contenido basado en ciertas condiciones:

{% if productos %}
    <ul>
        {% for producto in productos %}
            <li>{{ producto.nombre }} - {{ producto.precio }}€</li>
        {% endfor %}
    </ul>
{% else %}
    <p>No hay productos disponibles en este momento.</p>
{% endif %}

Este enfoque mejora la experiencia del usuario al proporcionar mensajes claros cuando no hay datos para mostrar.

Además de render(), existen otras funciones como redirect() y HttpResponse() que se utilizan en diferentes escenarios dentro de los controladores, pero render() es la más común para trabajar con templates.

Es importante asegurarse de que los nombres de las plantillas y su ubicación coincidan con lo especificado en los controladores para evitar errores. Utilizar rutas completas y explícitas dentro del directorio templates ayuda a Django a encontrar y renderizar las plantillas correctamente.

Por último, es buena práctica mantener un diseño coherente y reutilizable en las plantillas, aprovechando la herencia de plantillas que ofrece Django. Esto permite definir una estructura base y extenderla en otras plantillas, facilitando el mantenimiento y la consistencia en la aplicación.

Sintaxis básica de DTL

El Django Template Language (DTL) es un lenguaje de plantillas diseñado para generar contenido dinámico de manera segura y eficiente. Para aprovechar al máximo DTL, es fundamental entender su sintaxis básica, que incluye el uso de variables, filtros, etiquetas y comentarios.

Las variables en DTL se utilizan para mostrar datos dinámicos en las plantillas. Se delimitan con doble llave de apertura y cierre {{ }}. Por ejemplo:

<p>Hola, {{ usuario.nombre }}!</p>

En este ejemplo, se muestra el nombre del usuario proporcionado en el contexto desde la vista.

Los filtros permiten modificar o formatear las variables antes de mostrarlas. Se aplican utilizando el símbolo de tubería |. Por ejemplo:

<p>Fecha de registro: {{ usuario.fecha_registro|date:"d/m/Y" }}</p>

Aquí, el filtro date formatea la fecha según el formato especificado. Algunos filtros comunes incluyen:

  • upper y lower: convierten cadenas a mayúsculas o minúsculas.
  • truncatechars: limita el número de caracteres mostrados.
  • default: proporciona un valor por defecto si la variable es vacía.

Las etiquetas son instrucciones que controlan la lógica dentro de las plantillas. Se delimitan con {% %} y permiten realizar operaciones más complejas. Algunas etiquetas básicas son:

  • if: para evaluaciones condicionales.
  {% if usuario.is_staff %}
    <p>Bienvenido, administrador.</p>
  {% else %}
    <p>Bienvenido, usuario.</p>
  {% endif %}
  • for: para iterar sobre listas o conjuntos.
  <ul>
    {% for producto in lista_productos %}
      <li>{{ producto.nombre }} - {{ producto.precio }}€</li>
    {% endfor %}
  </ul>
  • include: para incluir otros archivos de plantilla.
  {% include "mi_app/navbar.html" %}
  • comment: para añadir comentarios que no se renderizan en el HTML.
  {% comment %}
    Este es un comentario que no será visible en el output.
  {% endcomment %}

Dentro de las etiquetas if, se pueden utilizar diversos operadores lógicos y de comparación:

  • Igualdad: ==
  • Desigualdad: !=
  • Mayor que, menor que: >, <
  • Contenido en: in, not in
  • Y, o: and, or

Por ejemplo:

{% if producto.stock > 0 and producto.disponible %}
  <p>Producto disponible para su compra.</p>
{% else %}
  <p>Producto agotado.</p>
{% endif %}

Es posible aplicar múltiples filtros en cadena sobre una variable:

<p>{{ texto|lower|truncatechars:30 }}</p>

Este ejemplo convierte texto a minúsculas y luego lo trunca a 30 caracteres.

Por defecto, DTL aplica autoescapado para prevenir inyecciones de código y vulnerabilidades XSS. Si necesitas mostrar contenido HTML de forma segura, puedes utilizar el filtro safe:

<p>{{ contenido_html|safe }}</p>

Pero es crucial usar safe solo cuando se está seguro de la procedencia y seguridad del contenido.

Además de la etiqueta comment, también se pueden utilizar comentarios de una sola línea con {# #}:

{# Este es un comentario que no se mostrará en el HTML #}

DTL proporciona algunas variables especiales y funcionalidades adicionales, como:

  • {{ forloop.counter }}: disponible dentro de un bucle for, indica el número de iteración actual.
  <ul>
    {% for item in lista %}
      <li>{{ forloop.counter }}. {{ item }}</li>
    {% endfor %}
  </ul>
  • {{ csrf_token }}: se utiliza en formularios para prevenir ataques CSRF.
  <form method="post">
    {% csrf_token %}
    <!-- Campos del formulario -->
  </form>

Se puede acceder a los atributos de objetos y también llamar a métodos que no requieren parámetros:

<p>{{ usuario.get_full_name }}</p>

En este ejemplo, get_full_name es un método del modelo usuario que devuelve el nombre completo.

Además de los filtros incorporados, es posible crear filtros personalizados registrándolos en un archivo templatetags. Esto permite ampliar la funcionalidad de DTL según las necesidades específicas de la aplicación.

Las variables globales están disponibles en todas las plantillas gracias a los context processors. Por ejemplo, {{ request }} contiene el objeto de petición HTTP actual y se puede utilizar para acceder a información como el usuario autenticado:

{% if request.user.is_authenticated %}
  <p>Hola, {{ request.user.username }}!</p>
{% else %}
  <p>Bienvenido, visitante.</p>
{% endif %}

Sintaxis condicional e iterativa en DTL

Las plantillas de Django utilizan la sintaxis condicional e iterativa del DTL para controlar el flujo y la presentación del contenido de manera dinámica. Estas funcionalidades permiten mostrar u ocultar partes de la plantilla y repetir bloques según las condiciones o datos disponibles.

Condicionales con {% if %}

La etiqueta {% if %} se utiliza para evaluar expresiones y renderizar contenido basado en el resultado. La sintaxis básica es:

{% if condición %}
  <!-- Contenido si la condición es verdadera -->
{% endif %}

Es posible incluir una rama alternativa con {% else %}:

{% if usuario.is_authenticated %}
  <p>Bienvenido, {{ usuario.username }}.</p>
{% else %}
  <p>Por favor, inicia sesión.</p>
{% endif %}

Para manejar múltiples condiciones, se utiliza {% elif %}:

{% if puntuación >= 90 %}
  <p>Excelente.</p>
{% elif puntuación >= 70 %}
  <p>Bueno.</p>
{% else %}
  <p>Necesita mejorar.</p>
{% endif %}

Las expresiones en las condiciones pueden incluir operadores de comparación y lógicos:

  • Comparaciones: ==, !=, <, <=, >, >=
  • Operadores lógicos: and, or, not
  • Pertenencia: in, not in

Ejemplo con operadores lógicos:

{% if usuario.is_staff and usuario.is_active %}
  <p>Acceso de administrador concedido.</p>
{% endif %}

Se pueden anidar bloques condicionales para evaluar condiciones más complejas:

{% if producto.disponible %}
  {% if producto.stock > 0 %}
    <p>Producto disponible en stock.</p>
  {% else %}
    <p>Producto disponible bajo pedido.</p>
  {% endif %}
{% else %}
  <p>Producto no disponible.</p>
{% endif %}

Iteraciones con {% for %}

La etiqueta {% for %} permite iterar sobre secuencias, como listas, tuplas o QuerySets. La sintaxis básica es:

{% for elemento in secuencia %}
  <!-- Contenido para cada elemento -->
{% endfor %}

Por ejemplo, para listar artículos:

<ul>
  {% for artículo in artículos %}
    <li>{{ artículo.título }}</li>
  {% endfor %}
</ul>

Dentro del bucle for, se dispone de la variable especial forloop, que proporciona información sobre la iteración actual:

  • forloop.counter: número de la iteración actual (empezando en 1)
  • forloop.counter0: número de la iteración actual (empezando en 0)
  • forloop.revcounter: cuenta regresiva desde el total hasta 1
  • forloop.first: True si es la primera iteración
  • forloop.last: True si es la última iteración

Ejemplo usando forloop:

<table>
  <tr>
    <th>N.º</th>
    <th>Nombre</th>
  </tr>
  {% for estudiante in estudiantes %}
  <tr {% if forloop.even %}class="par"{% endif %}>
    <td>{{ forloop.counter }}</td>
    <td>{{ estudiante.nombre }}</td>
  </tr>
  {% endfor %}
</table>

En este ejemplo, se aplica una clase CSS "par" a las filas pares utilizando forloop.even.

Manejo de listas vacías

Si la secuencia está vacía, es importante gestionar este caso para mejorar la experiencia del usuario. Se puede utilizar {% empty %} dentro de la etiqueta for:

<ul>
  {% for mensaje in mensajes %}
    <li>{{ mensaje.contenido }}</li>
  {% empty %}
    <li>No hay mensajes disponibles.</li>
  {% endfor %}
</ul>

Acceso a elementos de diccionarios y atributos

Al iterar sobre diccionarios o objetos, se puede acceder a claves y atributos:

{% for clave, valor in diccionario.items %}
  <p>{{ clave }}: {{ valor }}</p>
{% endfor %}

Uso de filtros en bucles

Es posible aplicar filtros a las secuencias dentro del bucle for:

{% for comentario in comentarios|slice:":5" %}
  <p>{{ comentario.autor }}: {{ comentario.texto }}</p>
{% endfor %}

Este ejemplo muestra solo los primeros cinco comentarios.

Comentarios sobre el rendimiento

Usar etiquetas condicionales y bucles correctamente es fundamental para el rendimiento de las plantillas. Evitar bucles anidados profundos y condicionales innecesarios puede mejorar la eficiencia.

Condicionales en línea

En algunas situaciones, se pueden utilizar condicionales en línea dentro de las variables:

<p>{{ usuario.nombre if usuario.is_authenticated else "Invitado" }}</p>

Sin embargo, esta sintaxis es más limitada y no soporta expresiones complejas.

Operadores de comparación y lógica

Es importante comprender cómo DTL interpreta las expresiones. Por ejemplo, al comparar valores, DTL utiliza la semántica de Python:

{% if lista %}
  <p>La lista tiene elementos.</p>
{% else %}
  <p>La lista está vacía.</p>
{% endif %}

En este caso, una lista vacía evalúa a False.

Control de flujo avanzado

Para condiciones más complejas, se pueden combinar operadores:

{% if producto.precio|floatformat:2 > 100.00 %}
  <p>Producto de lujo.</p>
{% endif %}

Aquí, el filtro floatformat se utiliza para formatear el precio antes de la comparación.

Macros y tags personalizadas

Si se necesitan funcionalidades más avanzadas, es posible crear etiquetas personalizadas utilizando la biblioteca de templatetags de Django. Esto permite encapsular lógica compleja y reutilizarla en las plantillas.

Buenas prácticas

  • Limitar la lógica en plantillas: mantener las plantillas lo más simples posible, delegando la lógica al contexto y las vistas.
  • Evitar lógica de negocio: la manipulación de datos debe realizarse en las vistas o modelos, no en las plantillas.
  • Usar comentarios: documentar secciones complejas con {% comment %} mejora la legibilidad.

Herencia en plantillas DTL

La herencia en plantillas es una característica poderosa del Django Template Language (DTL) que permite reutilizar y extender plantillas, facilitando la gestión y mantenimiento de la estructura de una aplicación web. Mediante la herencia, se define una plantilla base que contiene el esqueleto común de las páginas, y otras plantillas hijas que completan o modifican partes específicas.

El enfoque principal es crear una plantilla base que incluya elementos compartidos como la estructura HTML principal, cabeceras, pies de página y estilos. Luego, las plantillas hijas utilizan la directiva {% extends %} para indicar que heredan de la plantilla base, y pueden redefinir partes concretas utilizando bloques.

Una plantilla base típica podría llamarse base.html y contener la estructura general del sitio:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>{% block titulo %}Mi Sitio{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/estilos.css' %}">
</head>
<body>
    <header>
        <h1>Bienvenido a Mi Sitio</h1>
        <nav>
            <!-- Menú de navegación -->
        </nav>
    </header>

    <main>
        {% block contenido %}
        <!-- Contenido específico de cada página -->
        {% endblock %}
    </main>

    <footer>
        <p>© 2024 Mi Sitio. Todos los derechos reservados.</p>
    </footer>
</body>
</html>

En esta plantilla, se utilizan bloques mediante las etiquetas {% block %} y {% endblock %}. Los bloques definen secciones que las plantillas hijas pueden sobrescribir. En el ejemplo, hay dos bloques: titulo y contenido.

Para crear una plantilla hija que extienda base.html, se utiliza la directiva {% extends 'base.html' %} al inicio de la plantilla:

{% extends 'base.html' %}

{% block titulo %}Página de Inicio{% endblock %}

{% block contenido %}
<h2>Página de Inicio</h2>
<p>Este es el contenido de la página de inicio.</p>
{% endblock %}

En este caso, la plantilla hija redefine los bloques titulo y contenido. Al renderizarse, se tomará la estructura de base.html y se reemplazarán los bloques con el contenido proporcionado en la plantilla hija.

Se pueden definir tantos bloques como sea necesario en la plantilla base para segmentar las áreas que las plantillas hijas pueden personalizar. Por ejemplo, añadir bloques para estilos personalizados, scripts o elementos del encabezado:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>{% block titulo %}Mi Sitio{% endblock %}</title>
    {% block estilos %}
    <link rel="stylesheet" href="{% static 'css/estilos.css' %}">
    {% endblock %}
</head>
<body>
    <header>
        {% block encabezado %}
        <h1>Bienvenido a Mi Sitio</h1>
        {% endblock %}
        <nav>
            {% block menu %}
            <!-- Menú de navegación por defecto -->
            {% endblock %}
        </nav>
    </header>

    <main>
        {% block contenido %}
        <!-- Contenido principal -->
        {% endblock %}
    </main>

    <footer>
        {% block pie %}
        <p>© 2024 Mi Sitio. Todos los derechos reservados.</p>
        {% endblock %}
    </footer>
    {% block scripts %}
    <!-- Scripts por defecto -->
    {% endblock %}
</body>
</html>

Las plantillas hijas pueden elegir qué bloques redefinir según sus necesidades. Por ejemplo:

{% extends 'base.html' %}

{% block titulo %}Contacto{% endblock %}

{% block contenido %}
<h2>Contáctanos</h2>
<form action="/enviar/" method="post">
    {% csrf_token %}
    <!-- Campos del formulario -->
</form>
{% endblock %}

{% block scripts %}
<script src="{% static 'js/validaciones.js' %}"></script>
{% endblock %}

Es posible crear bloques anidados y utilizar la variable {{ block.super }} para mantener el contenido original del bloque de la plantilla base y agregar más contenido:

{% extends 'base.html' %}

{% block contenido %}
{{ block.super }}
<p>Contenido adicional específico de esta página.</p>
{% endblock %}

En este ejemplo, el contenido del bloque contenido incluye el contenido original gracias a {{ block.super }}, y agrega información adicional.

Aunque Django no soporta herencia múltiple en plantillas, se pueden lograr estructuras flexibles mediante la combinación de plantillas parciales y bloques. Las plantillas parciales son fragmentos reutilizables que se pueden incluir en otras plantillas usando {% include %}:

<!-- Archivo "_navbar.html" -->
<nav>
    <ul>
        <li><a href="/">Inicio</a></li>
        <li><a href="/contacto/">Contacto</a></li>
        <!-- Más enlaces -->
    </ul>
</nav>

Luego, en la plantilla base o en una hija:

{% include '_navbar.html' %}

Este enfoque permite modularizar componentes y mantener un código más limpio.

Supongamos una aplicación con varias secciones que comparten el mismo diseño. Crear una plantilla base y extenderla facilita la consistencia visual:

  • base.html contiene la estructura general.
  • inicio.html, sobre_nosotros.html, productos.html, etc., extienden base.html y proporcionan contenido específico.

Por ejemplo, inicio.html:

{% extends 'base.html' %}

{% block titulo %}Inicio - Mi Sitio{% endblock %}

{% block contenido %}
<h2>Bienvenido a Nuestra Empresa</h2>
<p>Descubre nuestros productos y servicios.</p>
{% endblock %}

Y productos.html:

{% extends 'base.html' %}

{% block titulo %}Productos - Mi Sitio{% endblock %}

{% block contenido %}
<h2>Nuestros Productos</h2>
<ul>
    {% for producto in productos %}
    <li>{{ producto.nombre }} - {{ producto.precio }}€</li>
    {% endfor %}
</ul>
{% endblock %}

Al utilizar la herencia de plantillas, se evita repetir el código común y se mejora la mantenibilidad.

Es recomendable organizar las plantillas en directorios que reflejen la estructura de la aplicación:

mi_proyecto/
├── mi_app/
    ├── templates/
        ├── mi_app/
            ├── base.html
            ├── inicio.html
            ├── productos.html
            ├── _navbar.html

Al referenciar plantillas, se utiliza la ruta relativa desde el directorio templates:

{% extends 'mi_app/base.html' %}

Esto previene conflictos de nombres y mejora la claridad.

Algunas buenas prácticas en la herencia de plantillas son:

  • Evitar redundancia: centralizar elementos comunes en la plantilla base.
  • Modularidad: usar plantillas parciales para componentes repetidos, como menús o pies de página.
  • Claridad: nombrar los bloques de manera descriptiva para facilitar su uso.
  • Simplicidad: mantener las plantillas lo más simples posible, delegando la lógica a las vistas y modelos.

Se pueden pasar variables de contexto a la plantilla base para personalizar elementos globales:

# En la vista
def vista_general(request):
    contexto = {
        'titulo_sitio': 'Mi Sitio',
        'usuario': request.user
    }
    return render(request, 'mi_app/inicio.html', contexto)
<!-- En base.html -->
<title>{% block titulo %}{{ titulo_sitio }}{% endblock %}</title>

De esta forma, las plantillas hijas pueden utilizar estas variables o redefinir el contenido de los bloques.

La herencia de plantillas puede ser en varios niveles. Una plantilla hija puede a su vez ser una plantilla base para otras plantillas. Esto es útil para secciones que comparten un diseño específico dentro del sitio. Por ejemplo:

  • base.html (plantilla raíz)
  • seccion_base.html (extiende base.html)
  • pagina_especifica.html (extiende seccion_base.html)

En seccion_base.html:

{% extends 'base.html' %}

{% block contenido %}
<div class="seccion-especial">
    {% block contenido_seccion %}
    <!-- Contenido de la sección -->
    {% endblock %}
</div>
{% endblock %}

Y en pagina_especifica.html:

{% extends 'seccion_base.html' %}

{% block contenido_seccion %}
<h2>Página Específica</h2>
<p>Contenido detallado de la página.</p>
{% endblock %}

Al utilizar estas técnicas, se consigue una mayor reutilización y organización del código de las plantillas.

Es importante recordar algunas etiquetas clave en la herencia de plantillas:

  • {% extends 'ruta/plantilla.html' %}: indica que la plantilla hereda de otra.
  • {% block nombre_bloque %}{% endblock %}: define un bloque que puede ser sobrescrito.
  • {{ block.super }}: referencia al contenido original del bloque en la plantilla padre.
  • {% include 'ruta/parcial.html' %}: incluye una plantilla parcial en la posición actual.

La herencia de plantillas es eficiente y recomendada. Django compila las plantillas y optimiza su uso, por lo que el impacto en el rendimiento es mínimo. Sin embargo, es importante:

  • Evitar bloques vacíos innecesarios: definir solo los bloques que se requieren.
  • Limitar la profundidad de herencia: aunque es posible tener múltiples niveles, una estructura demasiado profunda puede complicar el mantenimiento.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

  • Comprender la importancia de los templates en Django para separar la lógica de negocio de la presentación.
  • Configurar el entorno de plantillas en un proyecto Django, asegurando una estructura lógica y accesible.
  • Integrar plantillas en las vistas de Django utilizando la función render() con contextos dinámicos.
  • Utilizar el Django Template Language (DTL) para insertar contenido dinámico dentro de las plantillas, aprovechando variables, filtros y etiquetas.
  • Implementar estructuras condicionales e iterativas para gestionar la lógica de presentación de datos.
  • Aplicar buenas prácticas para el manejo y organización de templates, asegurando eficiencia y mantenibilidad en el desarrollo web.