Ricarica l'oggetto django dal database


161

È possibile aggiornare lo stato di un oggetto django dal database? Intendo un comportamento approssimativamente equivalente a:

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

AGGIORNAMENTO: Trovato un reopen / wontfix war nel tracker: http://code.djangoproject.com/ticket/901 . Ancora non capisco perché ai manutentori non piaccia.


In un normale contesto SQL, questo non ha senso. L'oggetto del database può essere modificato solo al termine della transazione e dopo aver effettuato un commmit. Una volta fatto ciò, dovresti aspettare la prossima transazione SQL per eseguire il commit. Perché farlo? Quanto tempo aspetterai la prossima transazione?
S. Lott,

Sembra una funzione inutile; è già possibile cercare nuovamente l'oggetto dal database.
Stephan,

mi piacerebbe anche questo, ma è stato chiuso più volte qui
eruciforme il

2
Non è appropriato perché gli oggetti del modello Django sono proxy. Se ottieni la stessa riga della tabella in due oggetti: x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), testeranno come uguali ma sono oggetti diversi e lo stato non è condiviso. È possibile modificare sia indipendentemente che salvarli: l'ultimo salvato determina lo stato della riga nel database. Pertanto è corretto ricaricare con assegnazione semplice - x1 = X.objects.get (id = 1). Avere un metodo di ricarica porterebbe molte persone a erroneamente dedurre che x1.f = 'nuovo valore'; (x1.f == x2.f) è True.
Paul Whipp,

Risposte:


260

A partire da Django 1.8 sono integrati oggetti di aggiornamento. Link ai documenti .

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 Sì, è stato implementato solo in 1.8. Per le versioni precedenti di Django è meglio andare con una delle altre risposte.
Tim Fletcher,

1
Non sei sicuro del significato di "Tutti i campi non rinviati" menzionati nei documenti?
Yunti

1
@Yunti Puoi rinviare i campi o chiedere esplicitamente solo un sottoinsieme di campi e l'oggetto risultante verrà popolato solo parzialmente. refresh_from_dbaggiornerà solo tali campi già popolati.
301_Moved_Permanently

Impossibile trovare i dettagli nei documenti, ma genera correttamente DoesNotExistun'eccezione se l'oggetto sottostante è stato eliminato durante la chiamata refresh_from_db. FYI.
Tim Tisdall il

28

Ho trovato relativamente facile ricaricare l'oggetto dal database in questo modo:

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

19
Sì, ma ... successivamente devi aggiornare tutti i riferimenti a questo oggetto. Non molto utile e soggetto a errori.
grep,

2
Ho ritenuto che ciò fosse necessario quando Celery ha aggiornato il mio oggetto nel db al di fuori di django, a quanto pare django ha tenuto una cache dell'oggetto poiché non aveva idea che fosse cambiato.
Bob Spryn,

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

1
@grep ha appena perso 2 ore a scrivere un test per questo caso d'uso: 1: inizializza un modello; 2: Aggiorna il modello tramite un modulo; 3: verifica che il nuovo valore sia aggiornato .... Quindi sì, soggetto a errori.
vlad-ardelean,

3
Penso che refresh_from_dbrisolva tutti questi problemi.
Flimm,

16

In riferimento al commento di @ grep, non dovrebbe essere possibile fare:

# 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

Grazie per la soluzione Se solo SO consentisse più voti positivi!
user590028,

11
Django ora fornisce il refresh_from_dbmetodo.
Flimm,

9

Come ha sottolineato @Flimm, questa è una soluzione davvero fantastica:

foo.refresh_from_db()

Ciò ricarica tutti i dati dal database nell'oggetto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.