En el anterior tutorial vimos cómo indicarle a Django donde tenía que ir dependiendo de la url pero la manera de ofrecer la respuesta no fue óptima. No debemos usar una variable de texto con el html, eso está feo y mal hecho. Hoy vamos a ver el uso de plantillas HTML para separar el código python del diseño. Aunque trabajemos con HTML, las plantillas pueden utilizarse para cualquier formato basado en texto como por ejemplo XML.
Configurando Django para indicarle donde están las plantillas
Lo primero que tenemos que hacer es decir a Django donde vamos a dejar nuestras plantillas. Para eso vamos a modificar el fichero settings.py y buscar la variable que se llame TEMPLATE_DIRS.
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ view settings.py [...] TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. ) [...]
Como podemos ver únicamente está rellena de comentarios que nos indican cómo trabajar con esta variable. Para los amantes de Windows fijaos como está indicando la ruta, ¿no os resultan raras esas barras idénticas a las de UNIX? También nos indica que debemos poner siempre rutas absolutas. Bien, vamos a rellenar esta variable:
Código :
TEMPLATE_DIRS = ( '/home/alberto/django/tutorial/templates', )
Si no os gustan las comas que ponemos al final de las tuplas podemos usar listas:
Código :
TEMPLATE_DIRS = [ '/home/alberto/django/tutorial/templates' ]
¿Qué pasaría si queremos mover nuestro proyecto a otro servidor y la ruta de directorios es distinta? Tendríamos que estar modificando todas las rutas absolutas que hayamos indicado pero tenemos otra solución. Modificad el fichero settings.py con lo siguiente:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial$ vi settings.py 1 # Django settings for tutorial project. 2 import os.path [...] 106 TEMPLATE_DIRS = [ 107 os.path.join(os.path.dirname(__file__) , 'templates').replace('\\','/') 108 ]
El modulo os.path nos da funcionalidades sobre rutas y ficheros. Por ejemplo join es una función que nos devuelve un objeto path uniendo sus parámetros en una ruta completa y dirname nos devuelve también un objeto path con la ruta del fichero que le pasemos por parámetro.
¿Y qué es la variable __file__? Esta variable es una variable reservada que contiene información sobre el fichero en el que se encuentra, en este caso settings.py.
¿Y la función replace? Pues por si estamos en Windows como hemos dicho las rutas no deben tener la contra-barra de ahí el cambio.
Acordaros de crear el directorio templates en la raíz de nuestro proyecto que es donde le hemos dicho que vamos a dejar las plantillas. Fácil. Sigamos.
Recuperando la plantilla desde nuestra vista - Forma antigua
Para generar correctamente una respuesta con plantillas, debemos asociar a nuestra plantilla un contexto, es decir, los datos que van asociados a nuestra plantilla. Este contexto siempre será un diccionario de par clave:valor. En caso de no llevar contexto nuestra respuesta lo generamos vacío.
Vamos a modificar la vista de suma para que usemos plantillas. El ejemplo es tan sencillo que no merecería la pena pero ya lo iremos complicando. Nuestro fichero firstView.py quedará así:
Código :
1 from django.template.loader import get_template 2 from django.template import Context 3 from django.http import HttpResponse 4 5 def holaMundo(request): 6 html = "<html><body>Hola Mundo desde DJANGO</body></html>" 7 return HttpResponse(html) 8 9 def suma(request,num1,num2): 10 op1 = int(num1) 11 op2 = int(num2) 12 template = get_template('suma.html') 13 response = template.render(Context({'result':op1+op2})) 14 return HttpResponse(response)
- Línea 1 y 2: importamos get_template y Context que nos servirán para recuperar y procesar la plantilla y para pasarle contexto a la plantilla.
- Línea 3: seguimos importando la clase HttpResponse porque la necesitamos también.
- Línea 12: utilizamos la función get_template para recuperar el fichero de plantilla de nuestro directorio templates.
- Línea 13: procesamos la plantilla y el contexto para generar la respuesta.
- Línea 14: devolvemos la respuesta como objeto HttpResponse.
De momento me seguís ¿no? Leemos la plantilla, recogemos los datos para esa plantilla, lo formateamos todo y lo convertimos en una respuesta válida para la vista.
Esto les parecía demasiado complicado a los chicos de Django y dijeron ahora vamos a hacer un paquete que se llame "accesos directos".
Recuperando la plantilla desde nuestra vista - Forma buena
Volvamos a cambiar nuestro fichero firstView.py ahora con este contenido:
Código :
1 from django.shortcuts import render_to_response 2 3 def holaMundo(request): 4 html = "<html><body>Hola Mundo desde DJANGO</body></html>" 5 return HttpResponse(html) 6 7 def suma(request,num1,num2): 8 op1 = int(num1) 9 op2 = int(num2) 10 response = render_to_response('suma.html',{'result':op1+op2}) 11 return response
- Línea 1: importamos del paquete shortcuts la función render_to_response. Esta función sera la encargada de realizar todos los pasos que hacíamos antes: recoger la plantilla, juntarla con el contexto y formar la respuesta http.
- Línea 10: la llamada a la función necesita la plantilla y un diccionario con los datos. Como podéis ver no hace falta convertirlo a Context. Esto lo hará solita nuestra función.
- Línea 11: ¡devolvemos la respuesta!
Ya tenemos configurado el servidor y tenemos hecha la vista solo nos queda generar la plantilla en HTML.
Creando nuestra plantilla
Vale pues creamos el siguiente fichero en nuestra carpeta templates dentro del proyecto:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial/templates$ view suma.html 1 <!DOCTYPE html> 2 <html lang="es"> 3 <head> 4 <meta charset="utf-8"> 5 <title>Mi primera plantilla</title> 6 </head> 7 <body> 8 La suma de los numeros es {{ result }}. 9 </body> 10 </html>
Fijaros en la línea 8. ¿Esa variable result nos resulta familiar? Es el mismo nombre que pusimos en el contexto pasado a la plantilla. Y si lo metemos dentro de {{ }} significa que queremos que escriba el valor en el html y a esto lo llamamos etiqueta. Ahora vemos más posibilidades.
Vale pues arrancad el servidor y vamos a ver qué pasa. Si lo teníais arrancado y habéis cambiado el fichero settings.py tendréis que reiniciar
Etiquetas
Ahora veremos que la mayor parte de etiquetas deberemos escribirlas entre {% %}. Hay excepciones como la que hemos visto ya que para que escriba el valor tendremos que usar {{ }} o por ejemplo los comentarios que son {# #}.
If / Else
Evalúa si una variable es False o True. Para que sea verdadero debe: existir, no estar vacía y no contener False.
Se puede usar con else y siempre debemos acabar con endif. Se aceptan and, or y not pero no pueden combinarse. Si necesitamos hacer distintas comprobaciones deberemos anidar los if.
Ejemplo:
Código :
{% if hoy_es_jueves %} <h2>Hoy hay mejorando.la!</h2> {% else %} <p> Oh! todavia no toca.</p> {% endif %}
Ejemplo2:
Código :
{% if sabado or domingo %} <h2>Fin de semana</h2> {% else %} {% if not viernes %} <p>Queda mucho para el fin de semana</p> {% else %} <p>El fin de semana esta cerca</p> {% endif %} {% endif %}
For
La sintaxis es "for x in y" donde y es el listado o secuencia a recorrer y x la variable que tomara el valor correspondiente a cada vuelta.
También se permite anidaciones de bucles aunque lo mejor es no complicarlos mucho, uno o dos niveles. Tenemos que terminar con endfor.
Ejemplo:
Código :
<ul> {% for nombre in lista nombres %} <li>{{nombre}}</li> {% endfor %} </ul>
El bucle for tiene una variable especial llamada forloop y que contiene información interesante del bucle. Vamos a ver sus posibilidades:
- forloop.counter : entero con el número de veces que se ha recorrido el bucle. Comienza con el valor 1.
- forloop.counter0: igual que el anterior pero empezando en 0.
- forloop.revcounter: entero con el número de "vueltas" que nos faltan para terminar el bucle. En la última vuelta toma el valor 1.
- forloop.revcounter0: igual que el anterior pero acaba en 0.
- forloop.first: valor booleano que contiene True si es la primera vuelta
- forloop.last: valor booleano que contiene True si es la última vuelta
- forloop.parent: en bucles anidados referencia a la variable forloop del bucle padre.
Ifequal/ Ifnotequal
Compara dos valores y devuelve True o False según sea el resultado. También permite else y hay que terminar con endifequal o endifnotequal.
Código :
{% ifequal user currentuser %} <p>Bienvenido de nuevo</p> {% endifequal %}
Se pueden comparar variables, strings, enteros y decimales:
Código :
{% ifequal user 'Paco' %} {% ifnotequal resultado 10 %} {% ifequal pi 3.14 %}
Comentarios
La única pega es que no pueden ser multilínea. Si queremos varias líneas de comentarios tendremos que encerrar cada línea entre {# #}.
Código :
{# Esto es #} {# un comentario valido #} {# Esto es un comentario no valido #}
Filtros
Cuando queremos escribir un valor dentro de nuestra plantilla es posible que queramos modificarlo antes. Con los filtros esto es posible, por ejemplo, formatear una fecha, poner todo el texto en mayúscula, etc.
Basta con poner los filtros separados por pipes detrás de nuestra variable.
Ejemplo:
Código :
{{ user|lower }} {# Esto escribe el nombre de usuario todo en minúsculas #}
Es posible que haya filtros que necesiten parámetros. Para eso usaremos la siguiente sintaxis: filtro:"<parametro>".
Algunos filtros interesantes son:
- addslashes: agrega contra-barras antes de cualquier contra-barra, comilla simple o comilla doble.
- date: formatea un objeto date o datetime con el formato que le pasemos por parámetro.
- escape: escapa caracteres a anotación html:
& -> &
< -> <
> -> >
" -> "
' -> %#39; - length: devuelve la longitud
- truncatewords: muestra el numero de palabras que le pasemos por parámetro, por ejemplo: {{ parrafo|truncatewords:"30" }}
- linebreaks
- lower
- ...
Un largo etc. Si necesitáis un filtro buscad en el api de django que seguro que existe.
Include
Permite en nuestra plantilla incluir contenido de otra. Aconsejo que uséis herencia en vez de este método (luego lo vemos).
Ejemplo:
Código :
<html> <body> {% include 'nav.html' %} [...] {% include 'footer.html' %} </body> </html>
Herencia de plantillas
La herencia permite escribir plantillas base o esqueletos y plantillas de contenido o bloques en ficheros separados. Este método es bastante más dinámico que el uso de la etiqueta include.
Vamos a modificar nuestro ejemplo de la suma para utilizar herencia.
Para ello vamos a crear un fichero que llamaremos esqueleto.html con este contenido:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial/templates$ view esqueleto.html 1 <!DOCTYPE html> 2 <html lang="es"> 3 <head> 4 <meta charset="utf-8"> 5 <title>{% block title %}{% endblock %}</title> 6 </head> 7 <body> 8 <h1>Usando herencia de plantillas</h1> 9 {% block content %} 10 <p>No hay contenido que mostrar</p> 11 {% endblock %} 12 </body> 13 </html>
- Línea 5: tenemos una nueva etiqueta, {% block %}. Esto indica que aquí hay un contenido que se debe modificar por la plantilla hija o se quedará vacío.
- Línea 9-12: tenemos otro bloque pero este va con contenido. ¿Qué significa? Significa que si no hay ningún hijo que lo sobrescriba se mostrara el contenido del padre. En este caso si ahora viéramos este html el titulo estaría vacio pero se mostraría que no hay contenido que mostrar.
Tenemos la plantilla padre, vamos a crear al hijo. Modificamos por completo nuestro fichero suma.html con el siguiente contenido:
Código :
alberto@a-AMILO-Si-3655:~/django/tutorial/templates$ view suma.html 1 {% extends 'esqueleto.html' %} 2 3 {% block title %}Sumando digitos con django{% endblock %} 4 5 {% block content %} 6 La suma de los numeros es <b>{{ result }}</b> 7 {% endblock %}
- Línea 1: etiqueta que indica que esta plantilla hereda de esqueleto.html. Debe ser la primera etiqueta si estamos creando una plantilla hija.
- Línea 3: sobrescribimos el bloque title de la plantilla padre
- Línea 5-7: sobrescribimos el bloque content de la plantilla padre. Eliminad estas líneas y mirad lo que pasa.
Bastante sencillo ¿verdad? Abrid el navegador con la url http://127.0.0.1:8000/suma/40/60/ y ya veréis.
Cosas que deberíamos saber:
- No se puede definir dos block con el mismo nombre en el mismo fichero. Esto es lógico porque volverías loco a django y te devolverá una excepción TemplateSyntaxError indicándote que el bloque X aparece más de una vez en el fichero Y.
- Podemos obtener el código o contenido del bloque padre para no sobrescribirlo por completo. Para recuperarlo tendremos que usar {{ block.super }}
Guau! Todo lo que hemos aprendido hoy. Es impresionante y solo estamos empezando. ¿Queréis más tutoriales?
Dudas, preguntas o lo que queráis estoy por cristalab o por twitter.
PD: Si queréis saber más sobre el paquete os.path -> http://docs.python.org/library/os.path.html
¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.
Por Carlos Aguilar el 10 de Febrero de 2012
Debes recordar que una aplicación Django se basa en acoplamiento débil y que poner todas las plantillas en un directorio principal en la raíz del proyecto limita la portabilidad de tu aplicación.
Por KalEl1285 el 10 de Febrero de 2012
Por ach4m0 el 11 de Febrero de 2012
KalEl, ¿por qué quieres complicarte la vida? Si hay herramientas para hacer mas facil nuestra vida deberiamos de usarlas, y django es una de ellas.
Por jsr02 el 05 de Marzo de 2012
Por devluis el 05 de Marzo de 2012
Por Jean Diaz el 17 de Marzo de 2012
Por Jean Diaz el 17 de Marzo de 2012
Por ach4m0 el 17 de Marzo de 2012
Respecto al error que te aparece se corresponde con que no encuentra la plantilla suma.html. ¿Puedes decirme que tienes en las siguientes variables de settings.py?
TEMPLATES_DIR
INSTALLED_APPS
¿Creaste el directorio templates dentro de la carpeta tutorial?
Por Jean Diaz el 17 de Marzo de 2012
también tengo creado la carpeta templates y dentro el suma.html
http://gyazo.com/a31f2629a067f2a96b8c42f2dd8fcac3
y estos son los contenidos de mis archivos:
http://gyazo.com/8d11ff0154da89ad1f889f6121d00f20.png?1332005760
Gracias por la ayuda!
Por ach4m0 el 18 de Marzo de 2012
Por sexitan0 el 18 de Marzo de 2012
En primer lugar, buen tutorial, me estás sirviendo de ayuda para iniciarme en Django. Por otro lado decirte que he seguido los pasos pero no me funciona el tema de la herencia, como padre (esqueleto.html):
<!DOCTYPE html>
<head>
<title>{% block title %}{% endblock %}</title>
<meta chartset="utf-8">
</head>
<body>
<header>
<h1>Usando herencia de plantillas</h1>
{% block content %}
<p>No hay contenido que mostrar</p>
{% endblock %}
</header>
<nav>
{% if result = 2 %}
<p>La suma es 2</p>
{% else %}
<p>La suma es distinta de: 2</p>
{% endif %}
</nav>
<section>
<p>Section</p>
</section>
<footer>
<p>Footer</p>
</footer>
</body>
</html>
y como hijo (suma.html):
{% extends 'esqueleto.html' %}
{% block title %}
Usando la herencia en Django
{% endblock %}
{% block content %}
La suma de los números es <b>{{ result }}</b>
{% endblock %}
si elimino la linea: La suma de los números es <b>{{ result }}</b> no me coge en le padre: <p>No hay contenido que mostrar</p>
¿qué hago mal?
Un saludo!!
Por ach4m0 el 18 de Marzo de 2012
Si no sirve esta solución dime el error que te da y lo que tienes en las variables TEMPLATES_DIR y INSTALLED_APPS
Por manuelm3z el 28 de Marzo de 2012