Django signals and fixtures

December 10, 2012

Django signals are one of the more powerful tools we have. They let you focus in what you want to do, and the keep you dry. They can be also a real nightmare when there are lots of them, not well documented, and even one signal could perform tasks that sends signals to other handlers. Under this circumstances testing is your only ally, but creating fixtures is a tedious work, well, creating fixtures that works on medium complex projects is always a pain, but if you have lots of signals connected, then it turns into hell.

As the project is complex, you can’t simply dumpdata, so you must write your fixtures “by hand”. When you do this, and try to load them with loaddata, you realize that some of the database objects you manually created in your fixture, are magically generated when the fixture is loaded, and then, when it tries to load what you wrote, it fails because the data is already on db. The question is that, when your fixtures are loaded, they send signals, so you will need to note this. However the django developers provided us a way to skip those signals when a operation such as syncdb or loaddata is in progress. Whenever this happens, the signal handler will receive an extra keyword in **kwargs raw, so you can use it to avoid running a signal handler like this.

@receiver(post_save, sender=ModelA)
def signal_handler(sender, **kwargs):
    raw = kwargs.get('raw', False)
    if not raw:
        <do whatever>

Unfortunately you need to do this in all the signals you want to skip. I’m considering do it in all the signals handlers that perform database inserts or updates.


Generando “fixtures” con sentido

April 25, 2012

Una de las cosas que menos me gusta de trabajar con Django es la generación de datos de prueba. Habitualmente cuando desarrollamos aplicaciones más o menos sencillas tenemos un servidor de explotación o producción, un servidor de pre-explotación o pre-producción y máquinas de desarrollo. Cuando la aplicación ya tiene cierta entidad, se hace necesario disponer de un conjunto de datos que podamos usar para comprobar el correcto funcionamiento de las distintas características ya sean nuevas o no. Además estos datos son también necesario para poder ejecutar los tests. Nunca he encontrado un mecanismo suficientemente bueno para, coger datos de producción, y pasarlos a los entornos de pre-producción o desarrollo.

He inspeccionado un poco por encima las alternativas que se muestran en Django Packages, pero ninguna me termina de parecer del todo buena. Muchas están centradas en la generación de datos para tests, que es tremendamente interesante, pero no es exactamente lo que estoy buscando.

A mi me gustaría tener un mecanismo mediante el que pudiera elegir unos cuantos objetos, y simplemente que todos los related a dichos objetos “vinieran” con ellos. De este modo si estuviera trabajando en un sitio en el que existiesen distintos tipos de roles, podría elegir un usuario de cada rol, y generar un conjunto de datos que me permita comprobar que cada uno puede hacer lo que se supone que puede hacer.

Alguien había publicado este snippet, pero no funciona debido a que utiliza partes internas de la api de django y cambiaron en la versión 1.3, así que me puse manos a la obra e hice lo mismo utilizando los mecanismos que existen ahora para hacer eso.

Este código es una acción para la interfaz administrativa, que se puede hacer disponible “site-wide”. Lo que hace es, dada una lista de objetos (qs), calcular la lista de objetos que se borrarían a través de “on cascade”, etc. Utiliza ese mecanismo, así que si se modifica el comportamiento ante borrado de un modelo probablemente no funcione adecuadamente.