Módulo templatetags
Para crear tags y filtros personalizados, la aplicación debe contener un paquete llamado templatetags:
catalogo/
templatetags/
__init__.py
catalogo_tags.py # Nuestros tags y filtros

El archivo __init__.py es obligatorio para que Python reconozca el directorio como paquete.
Filtros personalizados
Un filtro transforma el valor de una variable en la plantilla. Se registra con el decorador @register.filter:
# catalogo/templatetags/catalogo_tags.py
from django import template
from django.utils.safestring import mark_safe
import locale
register = template.Library()
@register.filter
def precio_formateado(valor):
"""Formatea un decimal como precio en euros: 1299.99 → '1.299,99 €'"""
try:
return f"{float(valor):,.2f} €".replace(',', 'X').replace('.', ',').replace('X', '.')
except (ValueError, TypeError):
return '—'
@register.filter(name='truncar_palabras')
def truncar_palabras(texto, num):
"""Trunca el texto a N palabras y añade puntos suspensivos."""
palabras = str(texto).split()
if len(palabras) <= num:
return texto
return ' '.join(palabras[:num]) + '…'
@register.filter
def es_multiplo(valor, divisor):
"""Devuelve True si valor es múltiplo de divisor."""
try:
return int(valor) % int(divisor) == 0
except (ValueError, TypeError, ZeroDivisionError):
return False
@register.filter(is_safe=True)
def estrellas(puntuacion, maximo=5):
"""Convierte una puntuación numérica en iconos de estrellas."""
puntuacion = min(int(puntuacion), maximo)
llenas = '★' * puntuacion
vacias = '☆' * (maximo - puntuacion)
return mark_safe(f'<span class="estrellas">{llenas}{vacias}</span>')
Uso en plantillas:
{% load catalogo_tags %}
<p>{{ producto.precio|precio_formateado }}</p>
<p>{{ producto.descripcion|truncar_palabras:20 }}</p>
<div>{{ resena.puntuacion|estrellas:5 }}</div>
{% for i in lista %}
{% if forloop.counter|es_multiplo:3 %}
<div class="nueva-fila"></div>
{% endif %}
{% endfor %}
Simple tags
Los simple_tags son como filtros pero pueden recibir múltiples argumentos y tienen acceso al contexto:
@register.simple_tag
def url_con_parametros(base_url, **kwargs):
"""Construye una URL con parámetros GET."""
from urllib.parse import urlencode
params = urlencode({k: v for k, v in kwargs.items() if v})
return f"{base_url}?{params}" if params else base_url
@register.simple_tag(takes_context=True)
def url_activa(context, url_name):
"""Devuelve 'active' si la URL actual coincide con url_name."""
from django.urls import reverse
try:
request = context['request']
return 'active' if request.path == reverse(url_name) else ''
except Exception:
return ''
Uso:
{% load catalogo_tags %}
<a href="{% url_con_parametros '/productos/' categoria='libros' orden='precio' %}">
Libros ordenados por precio
</a>
<a class="{% url_activa 'catalogo:producto-list' %}" href="{% url 'catalogo:producto-list' %}">
Productos
</a>
Inclusión tags
Los inclusion_tags renderizan una plantilla parcial con su propio contexto, perfectos para componentes reutilizables:
@register.inclusion_tag('catalogo/componentes/breadcrumb.html', takes_context=True)
def breadcrumb(context, *elementos):
"""Renderiza el breadcrumb de navegación."""
return {
'elementos': elementos,
'request': context.get('request'),
}
@register.inclusion_tag('catalogo/componentes/paginacion.html')
def paginacion(page_obj, parametros_url=''):
"""Renderiza los controles de paginación."""
return {
'page_obj': page_obj,
'parametros_url': parametros_url,
}
<!-- catalogo/componentes/paginacion.html -->
{% if page_obj.has_other_pages %}
<nav class="paginacion">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}&{{ parametros_url }}">‹</a>
{% endif %}
<span>{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&{{ parametros_url }}">›</a>
{% endif %}
</nav>
{% endif %}
Uso:
{% load catalogo_tags %}
{% breadcrumb "Inicio" "Catálogo" "Electrónica" %}
{% paginacion page_obj "categoria=electronica" %}
Cargar tags en todas las plantillas
Para no tener que escribir {% load catalogo_tags %} en cada plantilla, se pueden agregar las librerías en settings.py:
TEMPLATES = [
{
'OPTIONS': {
'builtins': [
'catalogo.templatetags.catalogo_tags',
],
},
},
]
Los template tags y filtros personalizados son la herramienta correcta para encapsular lógica de presentación compleja que sería inapropiado incluir directamente en las vistas o en el modelo.
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
Crear el módulo templatetags dentro de una aplicación Django. Registrar un filtro personalizado con @register.filter. Implementar template tags simples con @register.simple_tag. Crear inclusion_tags que renderizan una sub-plantilla con contexto propio. Usar los custom tags y filtros desde las plantillas con {% load %}.