2009-06-04 9 views
16

Gibt es eine Möglichkeit, Code nach dem Transaktions-Commit in Django auszuführen?Code nach dem Transaktions-Commit in Django ausführen

Ich muss einige Nachrichten an einen Rabbitmq-Server für die Offline-Verarbeitung senden, aber die Nachricht erhält den Verbraucher, bevor die Django-Transaktion festgeschrieben wird.

Meine Nachricht wird im post_save-Signal des Modells gesendet. Nach was ich suche, ist ein ähnlicher Mechanismus, der Signale oder etwas anderes verwendet, das Code nach dem Festschreiben ausführen würde (und nichts tun, wenn die Transaktion fehlschlägt).

Ich habe keinen generischen Weg gefunden, es in Django zu tun. Hast du eine Idee?

+1

Ich hatte ein simmilar Problem. Bei post_save speichert der Verleger (Prozess 1) den Aufgabenstatus und veröffentlicht die Nachricht. Consumer (Prozess 2) empfängt die Nachricht und aktualisiert den Task-Status, der noch nicht in der Datenbank enthalten ist. Was funktionierte, war, den Verbraucher für eine Sekunde oder zwei in den Schlaf zu versetzen, nachdem er die Nachricht erhalten hatte. Fühlt sich sowieso schmutzig an. – ohnoes

+0

Nachdem ich für 4 Stunden gekämpft habe, habe ich aus dieser Frage herausgefunden, dass wir keinen Code nach transaction.commit() haben können und jetzt läuft mein Code gut. Vielen Dank. – zubinmehta

+0

Zugehöriges Ticket: https: //code.djangoproject.com/ticket/14051 – guettli

Antwort

11

AKTUALISIEREN 2: django-transaction-hooks war merged into Django core und in Django Version 1.9 veröffentlicht.

UPDATE: django-transaction-hooks löst dieses Problem.

Ich glaube nicht, dass es einen sauberen Weg gibt, dies zu tun; zumindest kann ich nicht an einen denken. Sie können django.db.transaction.commit monkeypatch verwenden, um ein benutzerdefiniertes Signal zu senden. nicht schön, aber ich denke, es würde funktionieren.

Könnte auch interessant sein, diesen Anwendungsfall auf der django-developers mailing list zu erhöhen. Die Entwickler sind im Allgemeinen abgeneigt, neue Signale hinzuzufügen, aber Sie könnten hier einen guten Fall haben (und eine Widerlegung von einem Core-Entwickler könnte einen nützlichen Vorschlag enthalten, wie Sie Ihre Situation lösen können). Es ist wahrscheinlicher, dass Sie Antworten erhalten, wenn Sie bis nach dem 1.1 warten.

+0

Ich muss vorher an anderen Teilen meiner App arbeiten, aber ich werde diesem Pfad folgen. Wenn es fertig ist, werde ich eine E-Mail an django-Entwickler schicken und einen Fehlerbericht mit dem Patch hinzufügen. –

+0

Wenn 'django-transaction-hooks' eine zu hohe Django-Version erfordert, können Sie' django-db-signals' verwenden. – r3m0t

1

Eine Möglichkeit wäre, die Transaktions-Middleware so unterzuklassen, dass sie beim Commit ein benutzerdefiniertes Signal sendet. Ihr Code könnte auf dieses Signal achten und nicht auf post_save.

+0

Danke, ich denke, dass ich mit etwas Middleware gehen werde (ich werde wahrscheinlich eine weitere Middleware hinzufügen und keine Unterklasse der Transaktions-Middleware). Ich habe jedoch Bedenken wegen der Signale. Sind sie threadsicher? Wenn ein anderer Thread ein Signal ausgibt, darf der aktuelle Thread ihn fangen? –

+0

Ich habe immer noch ein Problem mit Middlewares: Wenn die Anwendung von einem Verwaltungsbefehl ausgeführt wird, wird es meine Rückrufe nicht ausführen. –

+0

Ich glaube nicht, dass deine Thread-Frage wirklich Sinn ergibt. Signale tun nichts besonderes in Bezug auf Threads. Ein in einem Thread gesendetes Signal ruft nur Empfänger in demselben Thread auf. Eingebaute Signalobjekte sind jedoch modulglobal, so dass ein Signal-Handler, der für post_save in einem Thread registriert ist, in allen Threads registriert ist. (Ich denke, es könnte möglich sein, Signalobjekte in Ihrem eigenen Code zu haben, die nicht global sind, haben sie nicht sorgfältig untersucht). –

1

Werfen Sie einen Blick auf django-celery-transactions für eine Lösung dieses Problems.

Ich habe kürzlich die Aufteilung und Refactoring der zugrunde liegenden Signale Code-Code in eine eigenständige Anwendung django-db-signals abgeschlossen.

5

Ich hoffe, dies kann jemandem helfen, der Django 1.9 oder später verwendet. Seit 1.9 steht on_commit zur Verfügung.

Also im Grunde würden Sie es wie folgt tun:

from django.db import transaction 

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

Wenn Sie post_save zu halten, können Sie immer noch on_commit verwenden:

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