Vientos de Libertad el blog de Juanjo Conti – en borrador permanente – abstracto, lúdico y digital
Browsing all posts in: Django

os.path en el settings.py de Django para mayor comodidad

Marzo 3

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.

Django Middleware

Agosto 15

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?

URLs elegantes con Django

Mayo 4

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.

FPDF en Django

Marzo 27

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')

El mejor hosting para Django

Noviembre 24

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

SQL Debug en Django

Noviembre 2

¿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

Diagrama de capas de Django

Octubre 30

¿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?

Generar diagramas de clases a partir de modelos de Django

Octubre 23

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.
Read the rest of this entry »

Streaming de datos en Django

Octubre 6

En PHP uno pude ir mandando datos al cliente (navegador) a medida que los va procesando en el servidor. En Django siempre creé el texto de la respuesta para luego pasársela al objeto HttpResponse. A pesar de que había preguntado varias veces en el canal de chat de Django si podía hacer algo así y me habían respondido que no, tenía la intuición de que algo se podría hacer y pensé en iteradores.

Por suerte este fin de semana tuvimos un gran evento de Python y alojé en mi casa a John, quien me explicó en 5 minutos cómo hacerlo. Aquí una recreación de su ejemplo (views.py):


from django.http import HttpResponse
from time import sleep

def gen():
    for x in range(80):
        sleep(1)
        yield '*' * x + '<br/>'

def test(request):
    return HttpResponse(gen(), mimetype='text/html')

Queda para el lector ver la salida en su navegador.
gen es una función que al llamarla devuelve un generador. El primer parámetro de HttpResponse puede ser un generador o cualquier objeto con el método __iter__.

En la misma jornada Nubis, estuvo explorando esto mismo desde distintos ángulos.

django-messages en español

Junio 28

django-messages es una aplicación que podemos integrar en nuestro proyecto Django para permitir a los usuarios enviarse mensajes entre ellos.

Hoy la traducimos con Ceci al español (es_AR, en realidad) mientras por IRC uno de los miembros del proyecto nos indicaba dónde tocar (nunca habíamos traducido una aplicación Django). Disponible desde la revisión 41.

Nuestra traducción, por si alguien tiene sugerencias o correcciones: es_AR.

Usando django-notification

Junio 28

django-notification es una aplicación para Django que permite al resto de las aplicaciones de un proyecto enviar notas a los usuarios. Así, cuando la aplicación Stock detecta que el número de bulones que hay en el depósito es menor límite de reposición puede enviar una nota de tipo “Avisos prioritarios” al usuario responsable del depósito. Esta nota puede ser recibida por mail, feed o mostrarse en el sistema cuando el usuario inicia su sesión.

El siguiente es un artículo del proyecto que hemos traducido yo y Cecilia.

Read the rest of this entry »

Generar archivos rtf en forma dinámica desde Django

Junio 27

En mi anterior artículo les conté sobre pyrtf-ng, una librería para generar archivos rtf en forma fácil desde un programa escrito en Python. Fue la primer alternativa que manejamos a la hora de plantearnos el problema de generar archivos rtf en forma dinámica desde Django.

En este artículo les cuento el aproach que finalmente adoptamos. Si la naturaleza dinámica del documento que queremos generar radica en que ciertas partes del texto tendrán valores dinámicos o ciertas partes pueden estar o no dependiendo de alguna condición, lo que podemos hacer es utilizar un sistema de templates. El formato rtf es un lenguaje de marcas (es texto, no binario!). Por lo que fácilmente podemos usar el subsistema de templates de Django para lograr nuestro objetivo. Read the rest of this entry »

Workaround para el bug 7233 de Django

Mayo 14

El bug se describe en http://code.djangoproject.com/ticket/7233, pero básicamente consiste en la imposibilidad de guardar objetos de tipo QueryDict en una sesión. Los usuarios de Django suelen toparse con el problema al intentar guardar en la sesión el objeto request.POST, yo lo hago de esta forma:

request.session['POST'] = dict(request.POST.items())

No sirve si se tienen múltiples valores para una clave porque items devuelve para cada clave el último valor almacenado.

En este caso, se puede usar lists:

request.session['POST'] = dict(request.POST.lists())

DjangoDash

Abril 15

Mi amigo Pupeno, sabe que estoy trabajando en Django y me contó sobre este concurso: DjangoDash. Consiste en crear una aplicación web en Django, en 48 hs, en equipos de hasta dos personas. ¿Quienes lanzaron la competencia? Toast Driven. A continuación una resumen en español de la página principal del concurso.
Read the rest of this entry »