ChatGPT解决这个技术问题 Extra ChatGPT

Reload django object from database

Is it possible to refresh the state of a django object from database? I mean behavior roughly equivalent to:

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

UPDATE: Found a reopen/wontfix war in the tracker: http://code.djangoproject.com/ticket/901. Still don't understand why the maintainers don't like this.

In an ordinary SQL context, this doesn't make sense. The database object can only be changed after your transaction finishes and does a commmit. Once you've done that, you'd have to wait around for the next SQL transaction to commit. Why do that? How long are you going to wait for the next transaction?
This seems like a needless function; it's already possible to just re-look-up the object from the database.
i would like this as well, but it has been shut down repeatedly here
It is not appropriate because Django model objects are proxies. If you get the same table row into two objects - x1 = X.objects.get(id=1); x2 = X.objects.get(id=1), they will test as equal but they are different objects and state is not shared. You can change both independently and save them - the last one saved determines the state of the row in the database. Therefore it is correct to reload with simple assignment - x1 = X.objects.get(id=1). Having a reload method would lead to many people wrongly inferring that x1.f = 'new value'; (x1.f == x2.f) is True.

T
Tim Fletcher

As of Django 1.8 refreshing objects is built in. Link to docs.

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 Yeah, it was only implemented in 1.8. For earlier versions of Django you're best going with one of the other answers.
Not sure what "All non-deferred fields are updated "mentioned in the docs means?
@Yunti You can defer fields, or explicitly ask for only a subset of fields and the resulting object will be only partially populated. refresh_from_db will only update such already populated fields.
Couldn't find details in the docs, but it properly throws a DoesNotExist exception if the underlying object was deleted when calling refresh_from_db. FYI.
A
Amandasaurus

I've found it relatively easy to reload the object from the database like so:

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

Yes, but... after that you have to update all references to this object. Not very handy and error-prone.
Found this to be necessary when Celery updated my object in the db outside of django, django apparently kept a cache of the object since it had no idea it had changed.
from django.db.models.loading import get_model; instance = get_model(instance).objects.get(pk=instance.pk)
@grep just lost 2 hours writing a test for this use case: 1: Initialize a model; 2: Update the Model via a Form; 3: Test that the new value is updated.... So yeah, error prone.
I think refresh_from_db solves all these problems.
R
Ron

As @Flimm pointed out, this is a really awesome solution:

foo.refresh_from_db()

This reloads all data from the database into the object.


E
Eloff

In reference to @grep's comment, shouldn't it be possible to do:

# 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

Thanks for the solution. If only SO allowed multiple up-votes!
Django now provides refresh_from_db method.