Dave
Dave

Reputation: 12638

Why do Django model-fields get out of sync when traversing relationships?

I'm running into an issue accessing a changed model field, traversing relationships in a Django 1.3 app. Changes committed to the database are not being reflected by in-memory objects. I'm using the User() object from Auth Middleware, linking it to a custom Profile() object:

User() <---one-to-one---> Profile()

The issue comes up when accessing the email field for a User():

$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> from myproject.myapp.models import Profile
>>>
>>> user = User.objects.get(pk=1)
>>> profile = user.profile
>>> user.email
u'[email protected]'                  # OK.
>>> profile.user.email
u'[email protected]'                  # OK.
>>> 
>>> user.email = '[email protected]'  # 1) Changes email address.
>>> user.save()                     # 2) Commits to database.
>>> user.email
'[email protected]'                   # 3) Changes reflected in user.email. Good.
>>> profile.user.email
u'[email protected]'                  # 4) Wrong. This is the old incorrect email.
>>> user.profile.user.email
u'[email protected]'                  # 5) Also wrong.
>>> 
>>> profile = Profile.objects.get(user=user)
>>> profile.user.email
u'[email protected]'                  # 6) If we re-query the DB, things are OK.

Why do things get out of sync in steps #4 and #5? I'm baffled by #5, going from the saved user object, to the profile, and back to the same saved user object.

Obviously there is some sort of caching going on, but I'm not sure of the logic/algorithm behind it. Anyone have an idea of what's happening, and the best way to approach this in code? Thanks for any insight you can offer! :)

Upvotes: 2

Views: 672

Answers (2)

Jonny Buchanan
Jonny Buchanan

Reputation: 62813

The SingleRelatedObjectDescriptor descriptor responsible for transparently looking up the related object checks for a cached version of the related object in _<fieldname>_cache in the Model instance, caching it the first time it is retrieved.

Django's ORM doesn't use an identity map, so changes to one Model instance aren't automatically reflected in other instances which existing references are held to.

Upvotes: 4

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799170

profile.user is not user. What you are looking at is another cached copy of the model that Django's ORM had previously pulled from the database. If you absolutely must have the latest value then you will need to requery each time.

Upvotes: 2

Related Questions