Comunidad de diseño web y desarrollo en internet online

Python en la web con Django (IV): uso de plantillas

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:
    & -> &amp;
    < -> &lt;
    > -> &gt;
    " -> &quot;
    ' -> %#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.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate