Ron
Ron

Reputation: 23516

Handle permission cache in django user model

I stumbled upon a weird behaviour: I add a permission to a user object but the permission check fails.

permission = Permission.objects.get_by_natural_key(app_label='myapp', codename='my_codename', model='mymodel')
user.user_permissions.add(permission)

user.has_permission('myapp.my_codename') # this is False!

I found some posts about user permission caching here and here and the solution seems to be to completely reload the object from the database.

# Request new instance of User
user = get_object_or_404(pk=user_id)

# Now note how the permissions have been updated
user.has_perms('myapp.my_codename') # now it's True

This seems really overkill for me and very un-django-like. Is there really no way to either clear the permission cache oder reload the foreign keys like you can do for an object with refresh_from_db()?

Thanks in advance!
Ronny

Upvotes: 8

Views: 2298

Answers (2)

Brian Destura
Brian Destura

Reputation: 12078

You can force the recalculation by deleting the user object's _perm_cache and _user_perm_cache.

permission = Permission.objects.get_by_natural_key(app_label='myapp', codename='my_codename', model='mymodel')
user.user_permissions.add(permission)
user.has_permission('myapp.my_codename') # returns False

del user._perm_cache
del user._user_perm_cache
user.has_permission('myapp.my_codename') # should be True

But this will essentially hit the database again to fetch the updated permissions. Since these are based on internal workings in django and not part of the public API, these cache keys might change in the future, so I would still suggest to just fetch the user again. But that's totally up to you.

Upvotes: 7

Dougyfresh
Dougyfresh

Reputation: 616

The caching is only an issue for you because of the has_perms method. If you follow it all the way down the stack, you eventually end up here. You'll see this method explicitly checking the cache before proceeding.

If you really need to know this user's permissions at this point in time and really don't want to refresh from the DB, then you can check more manually/directly without the has_perm helper method, since this permission .add() is a standard m2m operation and the model field has been updated.

In practice, it's unlikely you'll check a permission right after it is added, while the object is in scope, and while the permissions are cached since it's a bit redundant. I'm sure the Django devs considered this, and decided the benefits of caching it for you by default was the right call.

Upvotes: 2

Related Questions