Mini análisis de Django

Django es un en entorno de programación web que lleva varios años funcionando aunque ha sido ahora (2009) cuando ha visto un auge importante en su uso debido a la salida de la primera versión estable, la 1.0. En este artículo quiero dar una visión general y resumida de como está estructurado y como funciona, sin meterme en sintáxis, así como mi opinión personal-profesional. Es importante destacar que este artículo no es un curso ni un tutorial de Django, aunque sin duda tener unas nociones de como funciona Django en conjunto ayudará al que a continuación quiera aprenderlo a fondo.

Proyectos y aplicaciones

En Django tenemos los conceptos de proyectos y aplicaciones. Un proyecto engloba varias aplicaciones y las hace trabajar juntas para producir el resultado que ve el usuario, generalmente un portal web. Por ejemplo juanjoalvarez.net es un proyecto Django que está compuesto de aplicaciones para la interfaz de administración, blog, enlaces, comentarios, gestión de imágenes, visualización de mis artículos compartidos de Google Reader, visualización de mis tweets en Twitter, incrustación de objetos de la BBDD dentro de artículos (por ejemplo imágenes), exportación de feeds RSS y etiquetas (es probable que me deje algo.) Algunas de estas aplicaciones son de terceros (blog, imágenes, feeds del Google Reader, etiquetas, incrustación de objetos), otras venían con Django “de serie” (comentarios, interfaz de administración, exportación de RSS), otros los he desarrollado yo (Twitter y en el futuro cercano aplicación de reviews culturales) y otros los he mejorado (blog, incrustación). Es importante destacar el poco solapamiento que existe a priori entre estas aplicaciones (en cualquier momento puedo meter nuevas o sacar alguna existente sin afectar a los demás) y sin embargo lo fácil que es integrarlas.

Resumen de modo de operación de Django

El modo de operación real de Django es realmente más complejo de lo que voy a explicar aquí; sin embargo en este resumen se puede ver el flujo de los componentes principales usando un ejemplo realista. En diez pasos voy a especificar los pasos que daría el sistema desde que el usuario pincha en un link a “http://juanjoalvarez.net/blog” (o lo escribe en la barra de direcciones del navegador) hasta que visualiza la página ya cargada uno o dos segundos después.

  1. Cuando llega una petición a Django, por ejemplo un usuario carga http://juanjoalvarez.net/blog en su navegador, el servidor web (Apache normalmente) intercepta la petición y se la pasa a Django.

  2. Django obtiene la parte de la ruta (en éste caso /blog) y consulta un fichero interno llamado urls.py en el que básicamente se listan una serie de expresiones regulares (sencillas, generalmente) que asocian determinados patrones a las funciones Python que manejarán la petición que concuerde con ese patrón. Por ejemplo en mi fichero urls.py estará especificado que si la ruta es exactamente /blog/ el manejador deberá ser la función Python list_posts(category="blog").

  3. La función Python que maneja una petición (en Django se denominan vistas) recibe los parámetros que Django le pasa, que como vemos en este caso sería category="blog". También puede recibir, aunque en este caso no lo haga, un parámetro page que usaremos más tarde para definir en que página, de haber varias, se ha de iniciar la lista de artículos. Además Django automáticamente le pasa un objeto request con toda la información de la petición que ha realizado el navegador (como versión del navegador, cookies, parámetros GET y POST, sistema operativo, etc, etc, etc.)

    En el caso del ejemplo el parámetro category especifica que categorías de entradas se tienen que mostrar; cuando escribo un artículo entre los datos que introduzco (titular, texto, idioma, fecha de publicación, etc.) está el de las categorías a las que pertenece el mismo. Por ejemplo este mismo artículo pertenece a las categorías “articles”, “software libre” y “programación” (si bajas al final del texto del artículo las podrás ver y pinchar sobre ellas.) También existe la categoría “all” que mostraría todos los artículos sin importar la categoría a la que pertenezcan

    Siguiendo con el ejemplo, la categoría blog en este caso la aplico a entradas de lo que podría considerarse un blog como historias de mi vida o chorradas varias que no interesan a nadie (las cosas que pueden tener cierto interés como ésta las meto en “artículos”.) Al recibir éste parámetro la función Python list_posts() sabe que sólo debe mostrar las entradas que pertenezcan a esta categoría, de modo que usando una llamada le dice al ORM “dame todas las entradas que pertenezcan a la categoría blog”.

  4. El ORM, que es una capa intermedia por encima de la BBDD real (MySQL en este caso), proporciona una interfaz para acceder a la BBDD como si tratáramos con objetos. Al recibir la petición “dame todas las entradas que pertenezcan a la categoría blog” internamente va a hacer una consulta sobre la BBDD y obtendrá todos los registros de la tabla django_post. Pero en lugar de devolver esos registros en un diccionario, como hacen interfaces más primitivas a BBDD, lo que hace es crear un objeto Post por cada uno, inicializar ese objeto y cargar sus miembros con los valores del registro (realizando las conversiones pertinentes en cada caso de valores de BBDD y tipos Python), y genera estructuras de datos para acceder fácilmente a las claves externas y las relaciones muchos<=>muchos usando una interfaz Pythonica y sencilla.

    Además el ORM de Python (como cualquier otro buen ORM) no sólo nos permite usar registros de la BBDD como si fueran objetos sino que también nos permite definir éstos registros declarando clases Python, de modo que una versión simplificada de mi objeto Post podría ser:

    from models import *
    

    class Post(models.Model): title = CharField(max_length=255) author = ForeignKey(Author) entradilla = TextField() content = TextField() publish_date = DateTimeField(auto_now_add=True) categoria = ManyToManyField(Category)

  5. Cuando la capa ORM termina, la función list_posts() recibe la lista conteniendo todas las entradas que pertenecen a la categoría “blog” y como queremos mostrar los artículos paginados (para que no salen todos en la misma página sino sólo una cierta cantidad y enlaces a las otras páginas al final de la misma) le pasa esta lista a una función interna de Django llamada paginated_objects() que recibe una lista de objetos y devuelve un objeto “paginador”.

  6. list_posts() carga, usando otra función interna de Django, una plantilla llamada blog/posts_list.html y la renderiza usando el objeto paginador como argumento.

  7. El sistema de plantillas de Django procesa la plantilla posts_list.html, que es un fichero HTML con algunos tags especiales para iterar sobre listas de objetos, poner condicionales o visualizar objetos directamente, usando para el procesado el objeto paginador que se le ha pasado. El renderizado de plantillas leyendo el contenido ve que al principio se especifica que posts_list.html hereda de otra plantilla llamada base.html así que renderiza primero esta plantilla. base.html contiene toda la página salvo la parte de contenidos; en el caso de el fichero base.html de juanjoalvarez.net cargaría las hojas de estilo CSS y contendría código HTML para mostrar la cabecera, los menús, las cajas laterales (algunas de las cuales, como la de tweets y la de feeds, son dinámicas y para mostrarse llaman a su vez a otras funciones Python) y el píe de página, pero no especifica el contenido de la parte central.

    Una vez el sistema de plantillas de Django termina de renderizar la plantilla padre base.html vuelve a posts_list.html y ve que en esta plantilla se especifica que se ha de sobreescribir el bloque content de la plantilla padre. Este bloque en realidad está vacío en la plantilla base.html, pero está en la posición adecuada y rodeado de los div que especifican el estilo de la parte central, de modo que todo lo que se publique a ese bloque salga en la parte central de la página. posts_list.html contiene una iteración sobre la lista de entradas que proporciona el objeto paginated que ha recibido como argumentos, de modo que por cada uno de estos objetos, hasta el límite especificado por la paginación, genera el HTML para mostrar el título y la entradilla del artículo, así como la línea de separación entre ellos.

    Terminado el bucle que itera sobre las entradas de blog, la plantilla contiene otro bucle que por cada página que el objeto “paginador” especifique que hay, poner el número de página y un enlace que apuntará a /blog/page/x/ siendo x el número de página; en este caso la expresión regular del urls.py detectaría que hay que llamar a la función list_posts(category="blog", page=2) que haría exactamente lo mismo que antes pero le pasaría el parámetro page=2 al objeto paginador, de modo que este sepa que la lista de objetos a visualizar ha de empezar por la página dos.

  8. list_posts() recibe la salida del procesador de plantillas de Django y termina haciendo un return usando como argumento del return la plantilla ya renderizada, es decir, el HTML ya “desenrollado” conteniendo todas las entradas del blog paginadas y empotradas en el resto de la página, así como los enlaces a las distintas páginas en la parte inferior.

  9. Django recibe este HTML y a su vez se lo pasa a Apache (o el servidor web que usemos). Apache a su vez manda este HTML al navegador que visita la web.

  10. El navegador interpreta el HTML y se lo muestra al usuario.

Cuantas cosas para tardar un segundo o dos en total ¿verdad? Pero ahora vamos a ver que en realidad el programador no tiene que hacer tanto.

Componentes de una aplicación Django

Ya hemos visto por encima como funciona todo el proceso desde que el usuario pincha en un enlace hasta que Django le devuelve la página. Ahora vamos a ver como trabaja el esforzado programador para implementar ésto usando Django.

Como he comentado antes, en Django hay proyectos y aplicaciones. Normalmente vamos a trabajar sobre las aplicaciones (cada una en subdirectorio dentro del proyecto) y cuando la aplicación está implementada la activamos dentro de nuestro portal asociándola a una URL editando el fichero urls.py del proyecto.

Las aplicaciones normalmente se componen de los siguientes elementos:

  • *urls.py: * como he comentado antes, en este fichero se especifica que rutas de la URL van a cada función Python que las implementa. No todas las aplicaciones tienen que tener un fichero urls.py; por ejemplo podemos hacer que el urls.py del proyecto (directorio superior) contenga las asociaciones de URL con las vistas dentro de nuestra aplicación. Esto se suele hacer así para aplicaciones que no mapean muchas URLS, pero cuando nuestra aplicación necesite de varias URLS lo normal es que tenga su propio fichero urls.py y que el fichero de proyecto lo incluya especificando a partir de que ruta se va a usar el fichero de la aplicación.

    Por ejemplo si estuviéramos haciendo una aplicación para poner comentarios, en el urls.py del proyecto especificaríamos que para la ruta /comentarios se usen los mapeos del urls.py de la aplicación “comentarios”, y en el urls.py de ésta ya se mapearían / (la raíz, que en este caso sería /comentarios), /publicar (sería /comentarios/publicar), /borrar (/comentarios/borrar), etc.

    El holamundo del urls.py sería:

    from django.conf.urls.defaults import *
    from misvistas import holamundo
    

    urlpatterns = patterns('', (r'^holamundo/$', holamundo) )

  • *views.py: * en este fichero es donde va a estar el código que implementará la “inteligencia”, que curiosamente en Django llaman “vistas” (en la mayoría de otros entornos MVC lo llamarían “controlador") o la parte dinámica de cada página web. Siguiendo con el ejemplo anterior, la función que recibiera un comentario del formulario de “añadir comentario” que, muy creativamente, podríamos llamar “aniadir_comentario()” tendría que validar que todos los datos del mismo son correctos, validar que el usuario está autentificado, crear una instancia del modelo “comentario”, completar sus datos con los que hemos recibido del formulario, guardar el modelo en la BBDD y finalmente reenvíar al usuario a una página donde se le confirme que el comentario se ha puesto correctamente. Esto puede parecer mucho, pero una vista (recordemos, funciones Python normales) implementándolo puede que no sean más de quince líneas. Generalmente las vistas terminan devolviendo o bien un código HTTP (como 404, 500, etc) o una plantilla html a la que se le pasan una serie de datos.

    El holamundo de las vistas sería:

    def holamundo(request, nombre):
        saludo = 'Hola ' + nombre   
    
    # Devolvemos el HTML de la plantilla "plantillas/holamundo.html" 
    # con la variable de sustitución "saludo" cargada:
    
    return render_to_response('plantillas/holamundo.html', {}, RequestContext(request, {'saludo': saludo}))
    

  • *plantillas: * las plantillas son ficheros con formato HTML en los que podemos además poner código en el languaje de plantillas de Django, que es muy sencillo y principalmente vamos a usar haciendo sustituciones de variables o miembros de objetos que la vista le ha pasado a la plantilla usando dobles llaves (ej:< h2 > Título del artículo: {{articulo.titulo}} < /h2 >), o podemos poner algunas sentencias condicionales (if, ifqual, ifnotequal…) o iterativas (for). Aunque las plantillas pueden ser perfectamente ficheros HTML sin código de plantilla, generalmente las usaremos renderizadas al final de una vista y con ciertos valores que la vista le pasa para que los visualice. Por ejemplo si vamos a la página principal de juanjoalvarez.net veremos que en la parte central hay una lista de entradillas de artículos; estas se pasan como una lista de objetos Post (artículo) a la plantilla y en la plantilla hay un bucle que recorre esta lista y dentro de ese bucle algunas condicionales que me permiten ocultar un artículo si está marcado como estático, si se debe mostrar el artículo completo o sólo la entradilla (una variable miembro del modelo), etc, etc.

    El holamundo de las plantillas sería:

    &lt; html &gt;;
    &lt; body &gt;;
        Esta es la plantilla del holamundo y éste es mi amable saludo: {{saludo}}
    &lt; /body &gt;;
    &lt; /html &gt;;
    
  • *models.py: * aquí es donde vamos a definir los modelos que vamos a usar para nuestros datos, es decir, las clases que representarán nuestros datos (ejemplo para un blog: “Comentario”, “Articulo”, “Usuario”, “Bookmark”, etc.) En Django aunque podemos acceder directamente a la base de datos usando el lenguaje SQL casi nunca lo vamos a hacer así, sino que definiremos nuestras clases en el models.py de cada aplicación y ejecutaremos “manage.py syncdb” para que se “convierta” en tablas y registros SQL en nuestra base de datos real. Después desde nuestro código (es decir, desde nuestras vistas y bibliotecas) usaremos esos datos como objetos, con una sintaxis especial más cómoda que los select usando métodos de clase para obtener los objetos según los criterios que especifiquemos. Los modelos en Django se definen generalmente creando variables de clase como registros (el tipo se especifica con una asignación al tipo correcto, por ejemplo nombre = models.CharField(max_length=255)) aunque al ser una definición de clase normal también podemos añadir métodos que nos permitan interactúar de forma más sencilla con los objetos (ej: “articulo.validarHTML()”.)

    El holamundo de los modelos sería:

    from django.db 
    import models
    

    class Persona(models.Model): nombre = models.CharField(max_length=255) apellido = models.CharField(max_length=255) nacimiento = models.DateField()

Opinión personal-profesional

Lógicamente antes de poder hacer los pasos anteriores hemos tenido que instalar el proyecto Django (con un comando), configurar los settings básicos (editando el fichero settings.py), instalar las aplicaciones que necesitemos, adaptar sus respectivas plantillas para que se integren en la nuestra, sicronizar el modelo de datos con la BBDD real (con un comando), crear la plantilla base.html y alguna cosa más. Pero no mucho más. Ésta es la “belleza” de Django, una vez que uno entiende como funciona el framework y se empieza a hacer con el vocabulario de objetos y funciones ofrecidas por el entorno, las aplicaciones web y portales salen de forma extremadamente rápida, sin hacer chapuzas y siendo perfectamente mantenibles. Y por eso yo me encuentro entusiasmado con este framework, porque tras darle uso en varios proyectos no encuentro nada que me moleste ni que me haga gruñir especialmente. Todo está diseñado de forma elegante y sencilla, no hay nada de una complejidad espectacular (si uno tiene una cierta experiencia con Python, lenguaje muy fácil de aprender por otro lado), y sin embargo los resultados son casi siempre rápidos y correctos. Django tiene una carencia importantes de lo que yo llamo tchsss que son los puntos de un lenguaje, librería o framework que hacen que un programador al verlos haga tchsss. Django tiene alguno, pero comparado con otras cosas en informática tiene realmente pocos.

Así que ese es mi consejo: use Django, el entorno con menos tchsss

Links:

Contents