Laden Sie das Django-Objekt aus der Datenbank neu


159

Ist es möglich, den Status eines Django-Objekts aus der Datenbank zu aktualisieren? Ich meine Verhalten, das ungefähr gleichbedeutend ist mit:

new_self = self.__class__.objects.get(pk=self.pk)
for each field of the record:
    setattr(self, field, getattr(new_self, field))

UPDATE: Im Tracker wurde ein Wiedereröffnungs- / Wontfix-Krieg gefunden: http://code.djangoproject.com/ticket/901 . Ich verstehe immer noch nicht, warum die Betreuer das nicht mögen.


In einem normalen SQL-Kontext ist dies nicht sinnvoll. Das Datenbankobjekt kann erst geändert werden, nachdem Ihre Transaktion abgeschlossen ist und a commmit. Sobald Sie dies getan haben, müssen Sie warten, bis die nächste SQL-Transaktion festgeschrieben wird. Warum das tun? Wie lange warten Sie auf die nächste Transaktion?
S.Lott

Dies scheint eine unnötige Funktion zu sein; Es ist bereits möglich, das Objekt einfach aus der Datenbank erneut nachzuschlagen.
Stephan

Ich würde das auch mögen, aber es wurde hier
eruciform

2
Dies ist nicht angemessen, da Django-Modellobjekte Proxys sind. Wenn Sie dieselbe Tabellenzeile in zwei Objekte aufteilen - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), sie werden als gleich getestet, aber sie sind verschiedene Objekte und der Status wird nicht geteilt. Sie können beide unabhängig voneinander ändern und speichern - der zuletzt gespeicherte bestimmt den Status der Zeile in der Datenbank. Daher ist es richtig, mit einfacher Zuweisung neu zu laden - x1 = X.objects.get (id = 1). Eine Reload-Methode würde dazu führen, dass viele Menschen fälschlicherweise auf x1.f = 'neuer Wert' schließen. (x1.f == x2.f) ist wahr.
Paul Whipp

Antworten:


258

Ab Django 1.8 sind Aktualisierungsobjekte integriert. Link zu Dokumenten .

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)

@ fcracker79 Ja, es wurde nur in 1.8 implementiert. Für frühere Versionen von Django verwenden Sie am besten eine der anderen Antworten.
Tim Fletcher

1
Sie sind sich nicht sicher, was "Alle nicht zurückgestellten Felder werden aktualisiert" in den Dokumenten bedeutet?
Yunti

1
@Yunti Sie können Felder verschieben oder explizit nur eine Teilmenge von Feldern anfordern , und das resultierende Objekt wird nur teilweise ausgefüllt. refresh_from_dbaktualisiert nur solche bereits ausgefüllten Felder.
301_Moved_Permanent

Details konnten in den Dokumenten nicht gefunden werden, es wird jedoch eine DoesNotExistAusnahme ausgelöst, wenn das zugrunde liegende Objekt beim Aufruf gelöscht wurde refresh_from_db. Zu Ihrer Information.
Tim Tisdall

28

Ich fand es relativ einfach, das Objekt wie folgt aus der Datenbank neu zu laden :

x = X.objects.get(id=x.id)

19
Ja, aber ... danach müssen Sie alle Verweise auf dieses Objekt aktualisieren. Nicht sehr praktisch und fehleranfällig.
grep

2
Als Celery mein Objekt in der Datenbank außerhalb von Django aktualisierte, wurde dies als notwendig erachtet. Django behielt anscheinend einen Cache des Objekts, da es keine Ahnung hatte, dass es sich geändert hatte.
Bob Spryn

3
von django.db.models.loading import get_model; instance = get_model (instance) .objects.get (pk = instance.pk)
Erik

1
@grep hat gerade 2 Stunden beim Schreiben eines Tests für diesen Anwendungsfall verloren: 1: Initialisieren Sie ein Modell; 2: Aktualisieren Sie das Modell über ein Formular. 3: Testen Sie, ob der neue Wert aktualisiert wurde. Also ja, fehleranfällig.
Vlad-Ardelean

3
Ich denke, refresh_from_dblöst all diese Probleme.
Flimm

16

Sollte es in Bezug auf den Kommentar von @ grep nicht möglich sein, Folgendes zu tun:

# Put this on your base model (or monkey patch it onto django's Model if that's your thing)
def reload(self):
    new_self = self.__class__.objects.get(pk=self.pk)
    # You may want to clear out the old dict first or perform a selective merge
    self.__dict__.update(new_self.__dict__)

# Use it like this
bar.foo = foo
assert bar.foo.pk is None
foo.save()
foo.reload()
assert bar.foo is foo and bar.foo.pk is not None

Danke für die Lösung. Wenn nur SO mehrere Up-Votes erlaubt!
user590028

11
Django bietet jetzt refresh_from_dbMethode.
Flimm

9

Wie @Flimm betonte, ist dies eine wirklich großartige Lösung:

foo.refresh_from_db()

Dadurch werden alle Daten aus der Datenbank in das Objekt neu geladen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.