2010-01-08 14 views
9

Ein paar Mal bin ich in eine Situation geraten, wenn ich zum Zeitpunkt der Speicherung wissen muss, welche Modellfelder aktualisiert werden und entsprechend handeln.Verfolgen der Änderungen seit dem letzten Speichern in Django-Modellen

Die naheliegendste Lösung ist das primäre Schlüsselfeld zu nehmen und eine Kopie des Modells aus der Datenbank abrufen:

class MyModel(models.Model): 

    def save(self, force_insert=False, force_update=False, using=None): 
     if self.id is not None: 
      unsaved_copy = MyModel.objects.get(id=self.id) 
      # Do your comparisons here 
     super(MyModel, self).save(force_insert, force_update, using) 

Das funktioniert jedoch völlig in Ordnung, es die Datenbank Treffer für jede Instanz das Modell, das Sie speichern (ist möglicherweise ziemlich unbequem, wenn Sie viele solcher Speicherungen durchführen).

Es ist offensichtlich, dass, wenn man sich die alten Feldwerte zu Beginn der Lebensdauer der Modellinstanz (__init__) "merken" kann, es nicht notwendig sein sollte, eine Kopie des Modells aus der Datenbank zu erhalten. So kam ich mit diesem kleinen Hack up:

class MyModel(models.Model): 

    def __init__(self, *args, **kwargs): 
     super(MyModel, self).__init__(*args, **kwargs) 
     self.unsaved = {} 
     for field in self._meta.fields: 
      self.unsaved[field.name] = getattr(self, field.name, None) 

    def save(self, force_insert=False, force_update=False, using=None): 
     for name, value in self.unsaved.iteritems(): 
      print "Field:%s Old:%s New:%s" % (name, value, getattr(self, name, None)) 
     # old values can be accessed through the self.unsaved member 
     super(MyModel, self).save(force_insert, force_update, using) 

Dies scheint zu funktionieren, aber es ist die Verwendung der nicht-öffentlichen Schnittstelle von django.db.models.Model macht.

Vielleicht weiß jemand einen saubereren Weg, es zu tun?

Antwort

2

Ich denke, Ihre Lösung sieht vernünftig aus.

Alternativ könnten Sie eine Manager-Methode get_and_copy() (oder etwas) genannt haben, die eine Kopie des ursprünglichen Objekts aus der hing, was zurückgegeben wird. Sie könnten dann eine andere Manager-Methode verwenden, save_and_check(), die das kopierte Original ausnutzt.

FWIW: Wenn Sie mit Contrib/Admin-Vorlagen spielen, gibt es eine Kontextvariable namens original, die eine Kopie des ursprünglichen Objekts ist.

Update: Ich schaute genauer auf was Admin tut. In class ModelAdmin (befindet sich in Django/contrib/admin/options.py) gibt es eine Methode namens construct_change_message(). Es wird von formset.changed_data und formset.changed_objects angetrieben, so django/forms/models.py class BaseModelFormSet ist, wo die Aktion ist. Siehe die Methode save_existing_objects(). Schauen Sie sich auch die Methode _existing_object() an. Es ist ein wenig komplizierter als das, was ich zuvor erwähnt habe, da es sich um die Möglichkeit mehrerer Objekte handelt, aber im Grunde die Ergebnisse der Abfrage, die beim ersten Zugriff gesetzt wurde, zwischengespeichert werden.

+0

Nein, ich habe es nicht im Zusammenhang mit Admin-Vorlagen gefragt, aber ich werde überprüfen, wie es dort gemacht wird, danke für den Tipp. – shylent

0

Dies funktioniert nicht für Fixtures. loaddata Befehl verwendet models.Model.base_save. Wahrscheinlich wäre die sauberste Methode, Deskriptoren für Felder zu verwenden, aber man muss herausfinden, wie man sie richtig einfügt.

Verwandte Themen