221

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.

4
  • 1
    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? Commented Dec 7, 2010 at 15:55
  • This seems like a needless function; it's already possible to just re-look-up the object from the database. Commented Dec 7, 2010 at 18:10
  • i would like this as well, but it has been shut down repeatedly here Commented Jun 17, 2011 at 23:34
  • 2
    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. Commented Feb 6, 2014 at 20:49

4 Answers 4

363

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)
Sign up to request clarification or add additional context in comments.

4 Comments

@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.
31

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

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

8 Comments

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.
|
28

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

foo.refresh_from_db()

This reloads all data from the database into the object.

Comments

18

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

2 Comments

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.