A Django site.
Marzo 3, 2010
» os.path en el settings.py de Django para mayor comodidad

En el archivo de configuración settings.py de un proyecto Django, por lo general tenemos que setear variables como MEDIA_ROOT o STATIC_DOC_ROOT. Su contenido en una instalación Windows suele ser algo como: 'C:\Windows\camino\hasta\mi\projecto'. Y en Linux: '/home/usuario/camino/a/mi/proyecto'. El problema surge cuando el proyecto es desarrollado en varias máquinas a la vez, y con distintos sistemas operativos. Más aún, si hacemos lo anterior, seguramente versionaremos el proyecto y con él, al archivo de configuración. No sería raro que tras una actualización, el archivo se actualice con los valores que puso algún compañero de trabajo.

Mi solución es definir primero una variable para el proyecto:

PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))

Luego podemos usarla para definir el path absoluto a la carpeta con archivos de media:

MEDIA_ROOT = os.path.join(PROJECT_PATH, 'media')

nuestros templates:

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.
os.path.join(PROJECT_PATH, 'templates')
)

o cualquier otra variable de configuración que requiere una ruta de directorios.

Con esta solución podemos cambiar el proyecto de carpeta, disco o computadora y seguirá funcionando.

Agosto 15, 2009
» Django Middleware

Mientras estudié el tema para hacer un experimento, traduje el 80% de la documentación oficial. La dejo aquí más algunos condimentos personales.

Middleware

Es un sub framework que permite modificaciones al sistema de procesamiento de request/response de Django. Es un sistema de plugins liviano y de bajo nivel que permite alterar globalmente las entradas y salidas de Django.

Cada componente middleware es responsable de hacer alguna función específica.

Activar componentes middleware

Para hacerlo, añadirlo a la lista MIDDLEWARE_CLASSES en la configuración de Django (settings.py). En esta lista, cada componente se representa por un string: el camino completo al nombre de la clase. Por ejemplo:

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)

En el tratamiento de requests y la generación de responses, existen dos faces:

1) Fase Request (se llama a los métodos process_request() y process_view())
2) Fase Response (se llama a los métodos process_response() y process_exception())

En la primera, las clases son aplicadas desde la primera a la última, según el orden de la lista mencionada. En la segunda fase, se aplican en orden inverso; podemos pensarlo como una cebolla:

middleware

Una instalación de Django puede funcionar sin ningún middleware, pero esto no es recomendado.

Para escribir middleware propios

Cada componente es una clase Python, que no tiene que extender a ninguna clase en particular y debe definir uno o más de los siguientes métodos.

process_request(self, request)

request es un objeto HttpRequest. El método es llamada por cada request, antes de que Django decida que vista ejecutar.

Debe retornar None o un objeto HttpResponse. Si retorna None, Django seguirá procesando el request, ejecutando los otros middlewares y luego la vista apropiada. Si retorna un objeto HttpResponse, Django no hará nada más, solo retornar ese objeto.

process_view(self, request, view_func, view_args, view_kwargs)

request es un objeto HttpRequest. view_func es la función Python que Django está por usar. (Es el objeto function, no el nombre de la función en un string). view_args es un alista de argumentos posicionales que serán pasados a la vista. Y view_kwargs es un diccionario de argumentos de palagra clave que serán pasados a la vista. Ni view_args ni view_kwargs incluye al primer argumento de la vista (request).

process_view() es llamado antes de que Django ejecute la vista. Debe retornar None o un objeto HttpResponse. Si retorna None, Django seguirá procesando el request, ejecutando otros process_view() y luego la vista apropiada. Si retorna un objeto HttpResponse, Django no hará nada más, solo retornar ese objeto.

process_response(self, request, response)

request es un objeto HttpRequest. response es un objeto HttpResponse retornado por una vista de Django.

process_response() debe retornar un objeto HttpResponse. Puede altener el objeto response dado o puede crear uno nuevo.

A diferencia de proces_request() y process_view(), este siempre es ejecutado.

process_exception(self, request, exception)

request es un objeto HttpRequest. exception es un objeto Exception lanzado por la vista.

Django llama a process_exception() cuando la vista lanza una excepción. process_exception() debe retornar None o un objeto HttpResponse. Si retorna un objeto HttpResponse, la respuesta es devuelta al navegador. De lo contrario, el sistema por defecto para manejo de excepciones entra en acción.

__init__

Por lo general estas clases no tienen estado; simplemente contienen a los anteriores métodos. Se puede usar el método __init__ pero se debe tener en cuenta que Django inicializa estas clases sin argumentos y que el método __init__ es llamado solo una vez, cuando el servidor web arranca.

Experimento

Con lo anterior en mente, me quedaron algunas dudas al respecto:

  • si modifico los argumentos de view_func en process_view y retorno None, ¿estos son pasados modificados a la vista?
  • si lo anterior es falso, puedo lograr el mismo efecto haciendo:
    def process_view(self,request, view_func, view_args, view_kwargs):
        # modificar request, view_args y view_kwargs
        return view_func(request, view_args, view_kwargs)

    ?

  • ¿puedo definir process_response de tal forma que examine response?

Mayo 4, 2009
» URLs elegantes con Django

Este texto es parte del informe de nuestro proyecto final de carrera, lo publico en forma separada aquí por que tiene valor propio y puede convencer a más de un programador PHP de probar Django. Las URLs elegantes son la forma natural de las URL en Django.

Manejo de URLs

Tener URLs elegantes y limpias es un requisito común para aplicaciones web modernas. Django provee un mecanismo de manejo de URLs basado en expresiones regulares que asocia una expresión regular a una vista.

Para diseñar las URLs de una aplicación Django, se construye una especie de tabla que mapea patrones de URL a funciones Python a ejecutar (vistas). Con esto se logra que las URLs estén desacopladas del resto de la aplicación.

El siguiente es un ejemplo de una entrada de esa tabla:

(r'^index/$', index)

Suponiendo que la aplicación web está corriendo en el servidor con nombre localhost, cuando alguien acceda a http://localhost/index/ se ejecutará la vista ‘index’.

El siguiente es un ejemplo un poco más complejo:

(r'^cliente/datos/(\d+)/$', cliente_datos)

En el patrón de la URL se utilizan paréntesis para capturar una parte de la misma y poder accederla luego como parámetros en la vista. La expresión encerrada entre paréntesis se denomina grupo y son propias de las expresiones regulares en Python. Así, siempre que se acceda a, por ejemplo, http://localhost/cliente/datos/1/ o http://localhost/cliente/datos/100/ se ejecutará la vista cliente_datos y recibirá como parámetro el número correspondiente.

Algo similar sucede en el siguiente ejemplo:

(r'^inmueble/fotos/(\d+)/eliminar/(\d+)/$', eliminar_foto)

Con la diferencia de que ahora la vista recibe dos parámetros, en este caso particular el primero corresponde a un identificador de inmueble y el segundo a un identificador de foto.

Las expresiones regulares en Python soportan también lo que se denomina grupos nombrados. Esto permite obtener un grupo por su nombre. Si escribimos una especificación de URL como la siguiente:

(r'^inmueble/fotos/(?P<inmueble>\d+)/eliminar/(?P<foto>\d+)/$', eliminar_foto)

la vista será llamada utilizando parámetros nombrados. Esto tiene la ventaja de que si cambia el orden de los parámetros en la URL, la vista seguirá funcionando sin que se necesite redefinirla.

Marzo 27, 2009
» FPDF en Django

Si intentás usar FPDF en Django hay algunas cosas que necesitás saber:

  • (ya sabés qué) FPDF está originalmente escrita en PHP y permite generar documentos PDF sin usar PDFLib (C).
  • (ya sabés qué) hay más de un port de esta librería a Python. Todos son incompletos.
  • Usá este http://www.nsis.com.ar/svn/pyfpdf/ (parcheado en Argentina para utilizar unicode).
  • FPDF trabaja con la codificación ISO-8859-1.
  • Mi código fuente Django usa la cotificación UTF-8 y en los documentos resultantes aparecían caracteres raros en lugar de vocales con tilde o eñes.
  • Lo soluciné haciendo una modificacicón en el método Output:

self.buffer = buffer.encode('iso-8859-1')

Noviembre 24, 2008
» El mejor hosting para Django

Según DjangoFriendly, el mejor hosting para Django es Webfaction. Yo lo vengo probando por un mes y estoy muy satisfecho.

Noviembre 2, 2008
» SQL Debug en Django

¿Cómo saber en Django qué sentencias SQL se están ejecutando detrás de su ORM? Según la FAQ, podemos hacerlo de esta forma:

Make sure your Django DEBUG setting is set to True. Then, just do this:

>>> from django.db import connection
>>> connection.queries
[{'sql': 'SELECT polls_polls.id,polls_polls.question,polls_polls.pub_date FROM polls_polls',
'time': '0.002'}]

connection.queries is only available if DEBUG is True. It’s a list of dictionaries in order of query execution. Each dictionary has the following:

“sql“ — The raw SQL statement
“time“ — How long the statement took to execute, in seconds.

connection.queries includes all SQL statements — INSERTs, UPDATES, SELECTs, etc. Each time your app hits the database, the query will be recorded.

Cada vez que se realiza una nueva petición, esa variable es sobre escrita con las consultas que se ejecutaron en la vista asociada. La forma de verlas es accediendo a connection.queries en cada vista de nuestro interés. Para facilitar esta tarea y no tener código intrusivo, escribí un decorador:


from django.db import connection
def sql_debug(f):
'''
Decorador útil para inspeccionar las sentencias SQL que se ejecutan en
cada request.
'''
def inner(*args, **kwargs):
r = f(*args, **kwargs)
for d in connection.queries:
print "time: %s\n sql:%s\n\n" % (d['time'], d['sql'])
return r
return inner

Octubre 30, 2008
» Diagrama de capas de Django

¿Cuál sería un diagrama de capas correcto para representar una instalación de Django? Ensayé el siguiente gráfico; es para una instalación en particular, pero se puede cambiar GNU/Linux por Sistema Operativo, PostgreSQL por Base de Datos y Apache por Servidor Web para hacerlo más genérico:

La idea detrás de un diagrama de capas es expresar que los componentes de una capa le brindan servicios a los de la capa superior mediante alguna interfaz.
¿Mejores ideas? ¿Conviene otro tipo de diagrama?

Octubre 24, 2008
» Generar diagramas de clases a partir de modelos de Django

La siguiente es la mejor forma que encontré de tomar todos los archivos models.py de las aplicaciones que componen un proyecto Django y generar un diagrama de clases completo, listo para exportar cómo imagen. Utilizo un comando incluido en django-command-extensions.

Obtener e instalar django-extensions

Para Django 1.0 hay que bajar la última versión del svn:

svn checkout http://django-command-extensions.googlecode.com/svn/trunk/ django-command-extensions

E instalarlo ejecutando el script de instalación:

cd django-command-extensions
sudo python setup.py install

Podemos probar si se instaló correctamente abriendo una consola Python y ejecutando:

>>> import django_extensions

Para que este disponible en nuestro proyecto debemos agregar la aplicación a settings.py:

INSTALLED_APPS = (
...
'django_extensions'
)

Cuando ejecutemos:

python manage.py help

veremos una lista de los nuevos comandos disponibles.

Generar archivo .dot

Uno de ellos es graph_models el cual nos permitirá generar un archivo en formato .dot:

python manage.py graph_models -a > mi_proyecto.dot

Generar archivo .png

O una imagen png:

python manage.py graph_models -a -g -o mi_proyecto.png

Para que el anterior comando funcione, necesitamos tener instalado pygraphviz. En mi Ubuntu no tenía el paquete python-pygraphviz por lo que tuve que bajarlo desde su página web. Para instalarlo requiere tener instalados los paquetes graphviz y graphviz-dev.

Ejemplo

El siguiente comando genera el diagrama de clases para auth, la popular aplicación que viene con Django y sirve como ejemplo de cómo generar el diagrama para solo una aplicación del proyecto:

python manage.py graph_models auth -g -o mi_proyecto_auth.png

Nota

Les recomiendo bajar la versión de pygraphviz empaquetada de Cheeseshop en lugar de la del svn, ya que con esta el comando de django-extensions lanza una excepción. Correguí el programa, abrí un ticket y envié un parche. Para cuando leas esto probablemente ya se haya incorporado a trunk.