Załaduj ponownie obiekt django z bazy danych


160

Czy można odświeżyć stan obiektu django z bazy danych? Mam na myśli zachowanie mniej więcej równoważne z:

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

AKTUALIZACJA: Znaleziono ponownie otwartą / wontfix wojnę w trackerze: http://code.djangoproject.com/ticket/901 . Nadal nie rozumiem, dlaczego opiekunom to się nie podoba.


W zwykłym kontekście SQL nie ma to sensu. Obiekt bazy danych można zmienić dopiero po zakończeniu transakcji i wykonaniu commmit. Gdy już to zrobisz, będziesz musiał poczekać na zatwierdzenie następnej transakcji SQL. Dlaczego to robisz? Jak długo będziesz czekać na następną transakcję?
— S.Lott,

Wydaje się, że jest to niepotrzebna funkcja; jest już możliwe ponowne wyszukanie obiektu w bazie danych.
— Stephan

Chciałbym to za dobrze, ale został zamknięty wielokrotnie tutaj
— eruciform

2
Nie jest to właściwe, ponieważ obiekty modelu Django są proxy. Jeśli dostaniesz ten sam wiersz tabeli do dwóch obiektów - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), będą testowane jako równe, ale są to różne obiekty i stan nie jest udostępniany. Możesz zmienić zarówno samodzielnie, jak i zapisać - ostatni zapisany określa stan wiersza w bazie danych. Dlatego poprawne jest ponowne ładowanie z prostym przypisaniem - x1 = X.objects.get (id = 1). Zastosowanie metody przeładowania doprowadziłoby do tego, że wiele osób błędnie wywnioskowałoby, że x1.f = 'nowa wartość'; (x1.f == x2.f) jest True.
— Paul Whipp,

Odpowiedzi:


259

Od Django 1.8 odświeżanie obiektów jest wbudowane. Link do dokumentacji .

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 Tak, zostało zaimplementowane dopiero w 1.8. W przypadku wcześniejszych wersji Django najlepiej wybrać jedną z pozostałych odpowiedzi.
— Tim Fletcher

1
Nie wiesz, co oznacza „Aktualizacja wszystkich nieodroczonych pól” wspomniana w dokumentach?
— Yunti

1
@Yunti Możesz odroczyć pola lub wyraźnie poprosić o podanie tylko podzbioru pól, a wynikowy obiekt zostanie wypełniony tylko częściowo. refresh_from_dbzaktualizuje tylko takie już wypełnione pola.
— 301_Moved_Permanently

Nie można znaleźć szczegółów w dokumentacji, ale poprawnie zgłasza DoesNotExistwyjątek, jeśli podstawowy obiekt został usunięty podczas wywoływania refresh_from_db. FYI.
— Tim Tisdall

28

Zauważyłem, że relatywnie łatwo jest przeładować obiekt z bazy danych w następujący sposób:

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

19
Tak, ale ... po tym musisz zaktualizować wszystkie odniesienia do tego obiektu. Niezbyt poręczny i podatny na błędy.
— grep

2
Okazało się, że jest to konieczne, gdy Celery zaktualizował mój obiekt w bazie danych poza django, django najwyraźniej zachował pamięć podręczną obiektu, ponieważ nie miał pojęcia, że ​​się zmienił.
— Bob Spryn,

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

1
@grep właśnie stracił 2 godziny na pisaniu testu dla tego przypadku użycia: 1: Zainicjuj model; 2: Zaktualizuj model za pomocą formularza; 3: Sprawdź, czy nowa wartość została zaktualizowana .... Więc tak, podatne na błędy.
— vlad-ardelean

3
Myślę, że refresh_from_dbrozwiązuje wszystkie te problemy.
— Flimm

16

W odniesieniu do komentarza @ grep, czy nie powinno być możliwe:

# 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

Dzięki za rozwiązanie. Gdyby tylko TAK pozwolił na wiele głosów pozytywnych!
— user590028

11
Django udostępnia teraz refresh_from_dbmetodę.
— Flimm

9

Jak zauważył @Flimm, jest to naprawdę świetne rozwiązanie:

foo.refresh_from_db()

Spowoduje to ponowne załadowanie wszystkich danych z bazy danych do obiektu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.