#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 и хочу сразу же после этого, если метод, подлежащий тестированию, отреагирует соответствующим образом.