python - patches - Verfolgen der Änderungen seit dem letzten Speichern in Django-Modellen



python plot linspace (2)

Ein paar Mal bin ich auf eine Situation gestoßen, als ich zum Zeitpunkt der Speicherung wissen muss, welche Modellfelder aktualisiert werden und entsprechend handeln.

Die naheliegendste Lösung besteht darin, das Primärschlüsselfeld zu übernehmen und eine Kopie des Modells aus der Datenbank abzurufen:

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 ganz gut, aber es trifft die Datenbank für jede Instanz des Modells, das Sie speichern (könnte ziemlich unbequem sein, wenn Sie viele solcher Speicherungen machen).

Es ist offensichtlich, dass, wenn man sich zu Beginn der Lebensdauer der __init__ ( __init__ ) die alten Feldwerte "merken" kann, es nicht notwendig sein sollte, eine Kopie des Modells aus der Datenbank zu holen. Also kam ich auf diesen kleinen Hack:

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, jedoch nutzt es die nicht-öffentliche Schnittstelle von django.db.models.Model .

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

https://src-bin.com


Answer #1

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

Alternativ können Sie eine Manager-Methode namens get_and_copy() (oder etwas) verwenden, die eine Kopie des ursprünglichen Objekts von dem, was zurückgegeben wird, hängte. Sie könnten dann eine andere Manager-Methode verwenden, save_and_check() die das kopierte Original nutzt.

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

Update: Ich habe mir genauer angesehen, was Admin macht. In der 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 , also ist django / forms / models.py class BaseModelFormSet die Stelle, an der sich die Aktion befindet. Siehe die Methode save_existing_objects() . Schauen Sie sich auch die Methode _existing_object() . 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.


Answer #2

Dies funktioniert nicht für Fixtures. loaddata Befehl models.Model.base_save 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.