Обработка кэша разрешений в пользовательской модели django

#python #django #django-permissions

Вопрос:

Я наткнулся на странное поведение: я добавляю разрешение к объекту пользователя, но проверка разрешений не выполняется.

 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!
 

Я нашел несколько сообщений о кэшировании разрешений пользователей здесь и здесь, и, похоже, решение заключается в полной перезагрузке объекта из базы данных.

 # 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
 

Это кажется мне действительно излишним и совсем не похоже на джанго. Действительно ли нет способа очистить кэш разрешений или перезагрузить внешние ключи, как это можно сделать для объекта refresh_from_db() ?

Заранее спасибо!
Ронни

Комментарии:

1. Вы можете удалить кэш объекта пользователя для perms, чтобы принудительно пересчитать разрешения: del user._perm_cache; del user._user_perm_cache . Или вы можете написать свою собственную серверную часть модели, которая не кэширует пермы, но вы, вероятно, этого не захотите. ИМО, я думаю, что перезагрузка более удобочитаема/удобна в обслуживании

2. @bdbd Вы должны написать это в качестве ответа, я проверю это, и если это сработает, я приму это.

3. Поскольку вы поделились тем, что контекстом для этой потребности является модульный тест, можете ли вы поделиться ими? Может быть лучшее решение в зависимости от того, как написаны тесты

4. Здесь особо нечего показывать. Получите разрешение, назначьте разрешение тестируемому пользователю и вызовите метод и подтвердите результат. Но если это поможет, я могу опубликовать это.

Ответ №1:

Вы можете принудительно выполнить пересчет, удалив _perm_cache и пользовательского объекта _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
 

Но это, по сути, снова попадет в базу данных, чтобы получить обновленные разрешения. Поскольку они основаны на внутренней работе в django и не являются частью общедоступного API, эти ключи кэша могут измениться в будущем, поэтому я бы все равно предложил просто снова вызвать пользователя. Но это полностью зависит от вас.

Ответ №2:

Кэширование является проблемой только для вас из-за has_perms метода. Если вы проследите за ним весь путь вниз по стопке, вы в конечном итоге окажетесь здесь. Вы увидите, что этот метод явно проверяет кэш, прежде чем продолжить.

Если вам действительно нужно знать разрешения этого пользователя на данный момент времени и вы действительно не хотите обновляться из БД, то вы можете проверить больше вручную/напрямую без has_perm вспомогательного метода, так как это разрешение .add() является стандартной операцией m2m и поле модели было обновлено.

На практике маловероятно, что вы проверите разрешение сразу после его добавления, пока объект находится в области действия и пока разрешения кэшируются, поскольку это немного избыточно. Я уверен, что разработчики Django учли это и решили, что преимущества кэширования для вас по умолчанию были правильным решением.

Комментарии:

1. Спасибо за ваш ответ! На самом деле у меня есть проблема в модульных тестах, где я добавляю разрешение для тестового случая a и хочу сразу же после этого, если метод, подлежащий тестированию, отреагирует соответствующим образом.