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.

grep
quelle
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)
Tim Fletcher
quelle
@ 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)
Rory
quelle
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
Eloff
quelle
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.

Ron
quelle