Novedades en Django 1.3

Hace apenas una semana salio a la luz la versión 1.3 del framework de programación web Django.

Han sido varios meses de desarrollo y, fruto de ello, se han incorporado muchísimas novedades de gran importancia. Vamos a repasar en este artículo algunas de ellas.

Vistas basadas en clases

Hasta ahora las vistas se tenían que crear como funciones normales y corrientes. Aunque esto era más que suficiente, gracias a las nuevas vistas basadas en clases podremos definir vistas genéricas, y heredar otras a partir de ellas, reutilizando código.

Usarlas es muy simple. No hay más que crear una nueva clase que herede de la vista que queramos usar en cuestión. Django nos provee unas cuantas que posiblemente cumplan de sobra todas nuestras necesidades.

Por ejemplo, tenemos TemplateView, que es una vista que se encarga de cargar un template directamente (análogo al direct_to_template que teníamos antes). Para usarlo, podemos heredar de ella, y cambiar el template que tiene que cargar.

La página que carga la licencia de este mismo blog (es un HTML estático), podría estar declarada de la siguiente forma

from django.views.generic import TemplateView

class LicenciaView(TemplateView):
    template_name = "licencia.html"

Por su lado, el urls.py sería el siguiente:

from django.conf.urls.defaults import *
from blog.views import LicenciaView

urlpatterns = patterns('',
    (r'^about/license/', LicenciaView.as_view()),
)

Vemos que para cargar la vista tenemos que usar el método as_view, el cual, por cierto, nos ahorraría tener que heredar de la clase si lo único que vamos a hacer es sobrescribir atributos. En el ejemplo anterior, nos podríamos ahorrar el código en el views.py con lo siguiente:

from django.conf.urls.defaults import *
from django.views.generic import TemplateView

urlpatterns = patterns('',
    (r'^about/license/',
          TemplateView.as_view(template_name="licencia.html")),
)

Además podemos modificar el comportamiento de estas vistas de muchísimas otras formas, ya sea modificando el queryset que ejecuta, cambiando el nombre de la variable de contexto que tendrá el objeto principal o añadiendo nuevas variables de contexto que vayamos a necesitar (por poner algunos ejemplos).

class ArticulosView(ListView):
    context_object_name = "articulos"
    template_name = "articulos_list.html",

    def get_queryset(self):
        self.tag = get_object_or_404(Tag, name=self.args[0])
        return Articulo.objects.filter(tag=self.tag)

    def get_context_data(self, **kwargs):
        context = super(ArticulosView, self).get_context_data(**kwargs)
        context['tag'] = self.tag
        return context

El uso de estas clases genéricas puede llegar a ser mucho más complejo, dándonos muchísima más potencia que las anteriores vistas genéricas. Dado que podría dar para otro artículo entero, recomiendo leer la documentación oficial para entenderlas mejor.

Logging

En esta nueva versión de Django se incluye la posibilidad de realizar el logging del portal mediante el módulo oficial de Python.

Este módulo está compuesto por cuatro elementos básicos.

Logger
Este elemento identifica el recipiente donde se van a guardar todos los mensajes.
Handler
Una vez se genera un mensaje de error en el logger, este se ha de enviar a algún sitio. Estos lugares son los distintos handlers, que pueden ser la pantalla, un fichero del sistema, ...
Filters
Permiten filtrar que mensajes se mandan a cada uno de los handlers (podemos querer que solo alguno de ellos se muestren por pantalla, y que el resto se guarden en un fichero, por ejemplo).
Formatter
Define el formato de los mensajes.

Los mensajes tienen un nivel de criticidad, siendo los predeterminados DEBUG, INFO, WARNING, ERROR y CRITICAL. Estos niveles nos sirven, aparte de para clasificarlos, para poder restringirlos desde los loggers y handlers. Esto es así ya que ambos permiten indicar a partir de que criticidad admiten pasar los mensajes a través de ellos.

Para usar este módulo de logging desde Django, basta con generar el mensaje que queramos. Por ejemplo, si queremos guardar el momento en el que se entra en una vista en particular:

import logging
logger = logging.getLogger(__name__)

def mi_vista(request):
    logger.info('Hemos entrado en mi_vista')

Como podemos ver, a la hora de crear el logger hay que darle un nombre. Como estándar, se usa el del módulo en el que se está creando (referenciado por la variable __name__).

A continuación, ya sólo nos faltaría configurar los elementos anteriores. Esto se hace desde el settings.py, y puede a llegar a ser tan complejo como queramos (podemos añadir tantos loggers, handlers, filters y formatters como queramos).

Un ejemplo de esta configuración podría ser la siguiente:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'simple': {
            'format': '%(levelname)s: %(message)s'
        },
    },
    'handlers': {
        'console':{
            'level':'INFO',
            'class':'logging.StreamHandler',
            'formatter': 'simple'
        },
    },
    'loggers': {
        'proyecto.app': {
            'handlers': ['console'],
            'level': 'DEBUG'
        }
    }
}

En este ejemplo estamos creando lo siguiente:

  • Un formatter que escupe la información con la siguiente pinta: NIVEL: mensaje
  • Un handler que imprimirá los mensajes con criticidad INFO o superior (es decir, todos excepto los de debug) por la salida estándar (stdout).
  • Un logger llamado proyecto.app, que usará los elementos descritos previamente.

Y listo, nuestro sistema ya está funcionando correctamente.

Manejo de ficheros estáticos

Hasta ahora manejar los ficheros estáticos (CSS, JavaScript, imágenes, ...) era algo complicado en Django. Usualmente lo que se hacía era usar la variable MEDIA_ROOT del settings.py, y se colgaba en el directorio al que apuntaba todo el contenido, para a continuación, configurar el servidor web en cuestión para que sirviese ese directorio estáticamente. A esto había que añadirle el estar pendiente de que si usamos alguna aplicación de terceros que también tiene ficheros estáticos, hay que recolectarlos y colgarlos junto a los nuestros.

Este método tiene varios problemas.

  • Hay que guardar bajo la misma ruta los ficheros estáticos de la web, y los ficheros subidos por los usuarios del portal (ya que también se suele usar la variable MEDIA_ROOT para marcar el directorio raíz donde se guardan estos).
  • No hay forma de que las distintas aplicaciones autocontengan todo su contenido estático. Si descargamos alguna que necesite de, por ejemplo ficheros CSS o imágenes (algo muy común), tendremos que coger esos ficheros y colgarlos fuera de la carpeta de esa aplicación, dentro de nuestro MEDIA_ROOT.

Por esta y otras razones, Django ha incorporado una aplicación dentro de django.contrib llamada staticfiles, que justamente arregla todos estos males, aparte de añadir otros beneficios, como facilitar el servir los ficheros estáticos desde un servidor dedicado a ello.

Esta nueva aplicación añade entre otras cosas un nuevo comando al manage.py, llamado collectstatic, que se encarga de recolectar los ficheros estáticos de todas las aplicaciones instaladas y situarlos en una carpeta designada mediante una variable del settings.py. Así, de esta manera, se pude configurar el servidor web para que sirva esa carpeta de forma estática.

De igual manera, se han añadido nuevas variables de entorno a los templates para definir las rutas de los ficheros estáticos: STATIC_URL y STATIC_ROOT, manteniendo las anteriores MEDIA_URL y MEDIA_ROOT para los ficheros subidos por parte de los usuarios.

Mejoras de templatetags

Se han realizado mejoras en ciertos templatetags.

  • El tag include pasa a permitir un parámetro with mediante el cual añadir nuevas variables de contexto a la hora de cargar el fichero; y un parámetro only para indicar que solo se quieren usar las variables de contexto definidas por el with anterior (y no el resto de las existentes en el entorno en ese momento).
  • El tag with pasa a poder definir varias variables de contexto al mismo tiempo. Esto evita el tener que usar X bloques anidados de withs (donde X podía llegar a tender a mucho ;) ).
  • El tag load permite el argumento from permitiendo cargar solo ciertos templatetags de cierto módulo (y no todos, como actualmente se hacía).

Y mucho más

Y hasta aquí algunos de los cambios grandes que recibe esta nueva versión. Me dejo muchos en el tintero (gran mejora en el módulo de cachés, configuración del comportamiento del borrado en cascada, mejora del módulo de testing unitario, ...), así como otros muchísimos cambios menores y bugs solucionados, pero en algún momento había que acabar.

Para los que sigan teniendo curiosidad, recomiendo leerse la lista entera de cambios, en la web oficial de Django.

Comentarios