2009-06-04 13 views
16

¿Hay alguna forma de ejecutar código después de la confirmación de la transacción en Django?ejecutar el código después de la confirmación de la transacción en Django

Necesito enviar algunos mensajes a un servidor rabbitmq para el procesamiento fuera de línea, pero el mensaje llega al consumidor antes de que se haya comprometido la transacción de Django.

Mi mensaje se envía en la señal post_save del modelo. Lo que estoy buscando es un mecanismo similar, que use señales u otra cosa, que ejecute código después de la confirmación (y no haga nada si la transacción falla).

No he encontrado ninguna forma genérica de hacerlo en Django. ¿Tienes alguna idea?

+1

que tenía un problema simmilar. En post_save, Publisher (proceso 1) guarda el estado de la tarea y publica el mensaje. El consumidor (proceso 2) recibe el mensaje y actualiza el estado de la tarea, que aún no se encuentra en la base de datos. Lo que funcionó fue poner al consumidor a dormir por un segundo o dos, después de recibir el mensaje. Se siente sucio de todos modos. – ohnoes

+0

Después de luchar durante 4 horas, descubrí a partir de esta pregunta que no podemos tener código después de transaction.commit() y ahora mi código está funcionando bien. Gracias. – zubinmehta

+0

Boleto relacionado: https: //code.djangoproject.com/ticket/14051 – guettli

Respuesta

11

ACTUALIZACIÓN 2: django-transaction-hooks fue merged into Django core y lanzado en Django versión 1.9.

ACTUALIZACIÓN: django-transaction-hooks resuelve este problema.

No creo que haya una manera limpia de hacerlo; al menos no puedo pensar en uno. Puede enviar un parche a django.db.transaction.commit para enviar una señal personalizada; no es bonita, pero creo que funcionaría.

También podría ser interesante plantear este caso de uso en el django-developers mailing list. Los desarrolladores generalmente son reacios a agregar nuevas señales, pero es posible que tengas un buen caso aquí (y una refutación de un desarrollador central puede incluir una sugerencia útil de cómo resolver tu situación). Sin embargo, es más probable que obtengas respuestas si esperas hasta que salga el 1.1.

+0

Tengo que trabajar en otras partes de mi aplicación antes, pero seguiré este camino. Cuando esté listo, enviaré un correo electrónico a django-developers y agregaré un informe de fallas con el parche. –

+0

Si 'django-transaction-hooks' requiere una versión de Django demasiado alta, puede usar' django-db-signals'. – r3m0t

1

Una posibilidad sería subclasificar el middleware de transacción para que envíe una señal personalizada en la confirmación. Tu código podría escuchar esa señal, en lugar de post_save.

+0

Gracias, creo que iré con algún middleware (probablemente agregaré otro middleware y no subclases el middleware de transacción). Aunque me preocupan las señales. ¿Están seguros? Si otro hilo arroja una señal, ¿puede atrapar el hilo actual? –

+0

Todavía tengo un problema con middlewares: si la aplicación se ejecuta desde un comando de administración, no ejecutará mis devoluciones de llamada. –

+0

No creo que su pregunta hilo realmente tiene sentido. Las señales no hacen nada especial con respecto a los hilos. Una señal enviada en un hilo llamará a receptores en ese mismo hilo solamente. Sin embargo, los objetos de señal incorporados son de módulo global, por lo que un manejador de señal registrado para post_save en un hilo se registra en todos los hilos. (Creo que es posible tener objetos de señal en su propio código que no sean globales, no lo hayan examinado con cuidado). –

1

Tener un vistazo a django-celery-transactions para una solución a esto.

Recientemente he terminado de dividir y refactorizar el código de código de las señales subyacentes en una aplicación independiente django-db-signals.

5

Espero que esto ayude a alguien que use Django 1.9 o posterior. Desde 1.9 on_commit está disponible.

Así que, básicamente, que estaría haciendo de esta manera:

from django.db import transaction 

transaction.on_commit(
    lambda: send_msg_to_rabbitmqp(param1, param2, ...) 
) 

Si desea mantener post_save, todavía se puede utilizar on_commit:

@receiver(pre_save, sender=MyModel) 
def my_handler(sender, instance, created, **kwargs): 
    transaction.on_commit(
     lambda: send_msg_to_rabbitmqp(instance.id) 
    ) 
Cuestiones relacionadas