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.