最终,我的目标是扩展 Django 的 ModelAdmin 以提供字段级权限——也就是说,给定请求对象的属性和正在编辑的对象的字段值,我想控制字段/内联是否可见给用户。我最终通过向 ModelAdmin 添加一个方法并can_view_field()
修改内置方法来删除/排除用户没有权限(由 确定)查看的字段+内联来实现这一点。如果您想查看代码,我将它放在 pastebin中,因为它很长而且只是有点相关。get_form()
get_fieldset()
can_view_field()
它工作得很好......几乎。我似乎遇到了某种线程安全或缓存问题,其中 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
测试用例 1:重新启动服务器后,如果您首先以超级用户身份查看更改列表表单,您会看到表单应该发生,两者都public_field
可见secret_field
。如果您注销并以工作人员(但不是超级用户)的身份查看它,您只会看到public_field
.
测试用例 2:重新启动服务器,如果您先以员工身份登录,您仍然只能看到public_field
. 但是,如果您随后注销并以超级用户身份查看,您将看不到secret_field
. 这是 100% 可重现的。
我做了一些基本的线程安全诊断:
- 最后
get_form()
,我打印出了 ModelForm 对象的内存地址。应该如此,每个请求都是唯一的。因此,ModelForm 对象不是问题。 - 在管理员注册之前,我尝试打印 ModelAdmin 对象的内存地址。在测试用例 1 中,这两个请求都是唯一的。但是对于测试用例 2,它根本不会在第二个请求上打印。
在这一点上,我一无所知。我的下一个研究点将是管理员注册系统(我承认我对此一无所知)。状态会随着服务器重新启动而重置,所以似乎必须缓存 ModelAdmin?还是线程安全问题?如果我把它变成一个工厂并返回一个deepcopy()
ModelAdmin,它会为每个请求提供一个新的 ModelAdmin 吗?我一无所知,如果有任何想法,我将不胜感激。谢谢!