Recharger l'objet Django depuis la base de données

161

Est-il possible d'actualiser l'état d'un objet django à partir de la base de données? Je veux dire un comportement à peu près équivalent à:

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

MISE À JOUR: Nous avons trouvé une guerre reopen / wontfix dans le tracker: http://code.djangoproject.com/ticket/901 . Je ne comprends toujours pas pourquoi les responsables n'aiment pas ça.

grep
la source
Dans un contexte SQL ordinaire, cela n'a aucun sens. L'objet de base de données ne peut être modifié qu'une fois votre transaction terminée et après avoir effectué une opération commmit. Une fois que vous avez fait cela, vous devrez attendre que la prochaine transaction SQL soit validée. Pourquoi faire ça? Combien de temps allez-vous attendre la prochaine transaction?
S.Lott
Cela semble être une fonction inutile; il est déjà possible de simplement rechercher à nouveau l'objet dans la base de données.
Stephan
J'aimerais aussi cela, mais il a été fermé à plusieurs reprises ici
eruciform
2
Ce n'est pas approprié car les objets de modèle Django sont des proxys. Si vous obtenez la même ligne de tableau en deux objets - x1 = X.objects.get (id = 1); x2 = X.objects.get (id = 1), ils seront testés comme égaux mais ce sont des objets différents et l'état n'est pas partagé. Vous pouvez modifier les deux indépendamment et les enregistrer - le dernier enregistré détermine l'état de la ligne dans la base de données. Par conséquent, il est correct de recharger avec une affectation simple - x1 = X.objects.get (id = 1). Avoir une méthode de rechargement conduirait de nombreuses personnes à déduire à tort que x1.f = 'nouvelle valeur'; (x1.f == x2.f) est Vrai.
Paul Whipp

Réponses:

260

Depuis Django 1.8, des objets rafraîchissants sont intégrés. Lien vers la documentation .

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
la source
@ fcracker79 Oui, il n'a été implémenté qu'en 1.8. Pour les versions antérieures de Django, il est préférable d'utiliser l'une des autres réponses.
Tim Fletcher
1
Vous ne savez pas ce que signifie "Tous les champs non différés sont mis à jour" mentionnés dans la documentation?
Yunti
1
@Yunti Vous pouvez différer des champs, ou demander explicitement seulement un sous-ensemble de champs et l'objet résultant ne sera que partiellement rempli. refresh_from_dbne mettra à jour que les champs déjà remplis.
301_Moved_Permanently
Impossible de trouver les détails dans les documents, mais il lève correctement une DoesNotExistexception si l'objet sous-jacent a été supprimé lors de l'appel refresh_from_db. FYI.
Tim Tisdall le
28

J'ai trouvé relativement facile de recharger l'objet à partir de la base de données comme ceci:

x = X.objects.get(id=x.id)
Rory
la source
19
Oui, mais ... après cela, vous devez mettre à jour toutes les références à cet objet. Pas très pratique et sujet aux erreurs.
grep le
2
J'ai trouvé que cela était nécessaire lorsque Celery a mis à jour mon objet dans la base de données en dehors de django, django a apparemment gardé un cache de l'objet car il n'avait aucune idée qu'il avait changé.
Bob Spryn
3
de django.db.models.loading import get_model; instance = get_model (instance) .objects.get (pk = instance.pk)
Erik
1
@grep vient de perdre 2 heures à écrire un test pour ce cas d'utilisation: 1: Initialiser un modèle; 2: mettre à jour le modèle via un formulaire; 3: Vérifiez que la nouvelle valeur est mise à jour .... Donc oui, sujet aux erreurs.
vlad-ardelean
3
Je pense refresh_from_dbrésout tous ces problèmes.
Flimm
16

En référence au commentaire de @ grep, ne devrait-il pas être possible de faire:

# 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
la source
Merci pour la solution. Si seulement SO permettait plusieurs votes positifs!
user590028
11
Django fournit désormais une refresh_from_dbméthode.
Flimm
9

Comme @Flimm l'a souligné, c'est une solution vraiment géniale:

foo.refresh_from_db()

Cela recharge toutes les données de la base de données dans l'objet.

Ron
la source