5 python django django-admin django-permissions
最终,我的目标是扩展 Django 的 ModelAdmin 以提供字段级权限——也就是说,给定请求对象的属性和正在编辑的对象字段的值,我想控制字段/内联是否可见给用户。我最终通过向can_view_field()ModelAdmin添加一个方法并修改内置get_form()和get_fieldset()方法来删除/排除用户没有权限(由 确定can_view_field())查看的字段+内联来完成此操作。如果你想看代码,我把它放在了一个 pastebin 中,因为它很长而且只是有点相关。
它工作得很好......几乎。我似乎遇到了某种线程安全或缓存问题,其中 ModelAdmin 对象的状态以可重现的方式从一个请求泄漏到另一个请求。
我会用一个简单的例子来说明这个问题。假设我有一个模型,我使用字段级权限代码对其 ModelAdmin 进行了扩展。该模型有两个字段: - public_field,任何工作人员都可以看到/编辑 - secret_field,只有超级用户才能看到/编辑
在这种情况下,该can_view_field()方法将如下所示:
def can_view_field(self, request, obj, field_name):
"""
Returns boolean indicating whether the user has necessary permissions to
view the passed field.
"""
if obj is None:
return request.user.has_perm('%s.%s_%s' % (
self.opts.app_label,
action,
obj.__class__.__name__.lower()
))
else:
if field_name == "public_field":
return True
if field_name == "secret_field" and request.is_superuser:
return True
return False
Run Code Online (Sandbox Code Playgroud)
测试案例 1:重新启动服务器后,如果您首先以超级用户身份查看更改列表表单,您会看到该表单应该发生,同时显示public_field和secret_field可见。如果您注销并以工作人员(但不是超级用户)身份查看它,您只会看到public_field。
测试案例 2:重新启动服务器,如果您先以工作人员身份登录,您仍然只能看到public_field. 但是,如果你再注销,并查看作为一个超级用户,你就不会看到secret_field。这是 100% 可重复的。
我已经完成了一些基本的线程安全诊断:
get_form(),我打印出了 ModelForm 对象的内存地址。应该是,它对于每个请求都是唯一的。因此,ModelForm 对象不是问题。在这一点上,我无能为力。我的下一个研究点将是管理员注册系统(我承认我对此一无所知)。状态随着服务器重启而重置,所以似乎必须缓存 ModelAdmin?还是线程安全问题?如果我把它变成一个工厂并返回一个deepcopy()ModelAdmin,它会为每个请求提供一个新的 ModelAdmin 吗?我一无所知,将不胜感激任何想法。谢谢!
我很困惑为什么您认为 ModelAdmin 应该是每个请求的新实例。管理对象由admin.site.register(Model)每个 admin.py 中的调用实例化,而 admin.py 又从admin.autodiscover()urls.py 中调用。换句话说,这发生在进程启动时。鉴于大多数 Web 服务环境的动态多进程性质,您可能会也可能不会获得针对任何特定请求的新进程 - 当然您不会每次都获得一个新进程。
因此,在 ModelAdmin 这样的全局对象上存储或更改状态并不明智。我没有正确查看您的链接代码,但至少在一种情况下,您self由于方法调用而更改了属性。不要这样做 - 您需要找到一些其他方法在方法之间传递动态值。