2011-01-15 10 views
11

Ich versuche, eine kleine Anwendung zu schreiben, die Videodateien empfängt und sie in ein einheitliches Format konvertiert, nachdem sie hochgeladen wurden (also der Datenbank hinzugefügt). Ich habe im Internet nach der besten Lösung dafür gesucht und mich entschieden, Djangos Signale mit Celery zu verwenden. Aber jetzt versuche ich einen Proof-of-Concept zu erstellen, um zu sehen, ob es funktioniert.Django - post_init-Signal wird an Model-Instanz aufgerufen. Save & before-Instanz wird sogar erstellt. Warum?

Ich versuche, eine video_repalce() Methode auszuführen, nachdem ein neues Video hochgeladen wurde (daher wurde eine neue Zeile zur Datenbank hinzugefügt). Aber das Signal funktioniert nicht richtig, oder ich habe nicht verstanden, wie das ganze System funktioniert.

Ich verwende Django 1.2.3 mit vordefinierten Signal django.db.models.signals.post_init, die should be called after a model has been instantiated (So wurde eine neue Zeile in die Datenbank hinzugefügt).

from django.core.files.base import File 
from django.db.models.signals import post_init 
import os 
import os.path 
import subprocess 

class Project(models.Model): 
    video = models.FileField(upload_to="projects/videos") 

    def replace_video(self): 
     """Replace original video with an updated one.""" 

     # Video conversion process code goes here, 
     # resulting in a new external video file. 

     self.video.delete() # Delete the original video. 
     self.video.save("newfile.webm", File(open("path/to/newfile.webm") ,"wb"))) # Save the new video instead. 

     self.save() # Commit everything to database. 

     os.remove("path/to/newfile.webm") # Remove original video copy after it was commited (copied) into the DB. 

# ... 
# ... 

def handle_new_project(sender, **kwargs): 
    """Handels some additional tasks for a new added project. i.e. convert video to uniform format.""" 

    project = kwargs['instance'] 
    project.replace_video() 

# Call 'Project.replace_video()' every time a new project is added. 
post_init.connect(handle_new_project, sender=Project, dispatch_uid="new_project_added") 

jedoch post_init wird genannt nicht nur wenn ein neues Modell Instanz erstellt wird, aber auch ...:

  1. Bevor das Modell selbst instanziiert wird. Was ich meine ist, es heißt, wenn ich den Server zum ersten Mal execute, wenn es nicht einmal eine einzige Reihe von Daten in der Datenbank gibt (also sollten keine Model-Objekte instanziiert werden). Die Instanz self.pk ist None!
  2. Wenn save()-ein Modell. Der obige Code wird auch ausgeführt, wenn ich self.save() drücke.

Praktisch funktioniert es nicht gemäß den Dokumenten.

Was mache ich falsch? Denken Sie daran, dass dies ein Proof-of-Concept ist. Ich beabsichtige, den Code zu Celery zu verschieben, nachdem ich sehe, dass es funktioniert. Aber, wenn die Signale nicht richtig funktionieren, Celery wird nicht helfen - Das Signal wird immer ein paar Mal erneut gesendet werden, wenn ich save() oder ein Video aktualisieren.

Glaubst du, ich sollte nicht save() innerhalb der replace_video() Methode anrufen? Also, wo soll ich es nennen? Welches Signal sollte ich wählen? post_save ist keine gute Option, weil es auch aufgerufen wird, wenn ich save() treffe.

+0

Sie erwähnen, dass das Signal immer gesendet wird, wenn Sie den Server ausführen, was passiert, wenn Sie nur eine Shell 'manage.py shell' öffnen und das Modell importieren? –

Antwort

12

Sie scheinen ein wenig Verwirrung darüber zu haben, was es bedeutet, ein Objekt zu instanziieren. Es hat nichts mit der Datenbank zu tun. Dieses instanziiert ein Modellobjekt, ohne sie in die Datenbank zu speichern, wobei in diesem Fall seine pk None sein wird:

MyObject(field1='foo', field2='bar') 

und diese (indirekt) instanziiert ein Objekt, indem sie aus der Datenbank erhalten:

MyObject.objects.get(field1='baz') 

In beiden Fällen wird das Signal post_init gesendet, auch wenn keines davon etwas mit dem Speichern in der Datenbank zu tun hat.

Wenn beim Speichern etwas passieren soll, überschreiben Sie entweder die Methode save selbst oder verwenden Sie die Signale pre_save oder post_save. Sie können dort prüfen, ob das Objekt zuvor gespeichert wurde, indem Sie prüfen, ob pk None ist.

+0

Ich fand die Methode 'pk is None' immer etwas unheimlich (man kann sie manuell einstellen), aber ich denke, es ist die einzige Möglichkeit. – vicvicvic

+0

Ich habe schließlich 'post_save' verwendet und eine Bedingung hinzugefügt, um zu überprüfen, ob ich das Video ersetzen muss. Daher rufe ich 'save()' nicht rekursiv auf. –

Verwandte Themen