ModelAdmin线程安全/缓存问题

时间:2010-08-02 13:15:50

标签: python django django-admin django-permissions

最终,我的目标是扩展Django的ModelAdmin以提供字段级权限 - 即,给定请求对象的属性和正在编辑的对象的字段值,我想控制字段是否/内联对用户可见。我最终通过向ModelAdmin添加can_view_field()方法并修改内置get_form()get_fieldset()方法来删除/排除用户没有权限的字段+内联(如由can_view_field()确定。如果您想查看代码,我会将其放置in a 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

测试用例1:重新启动服务器时,如果您首先以超级用户身份查看更改列表表单,则会看到表单应该发生,public_fieldsecret_field都可见。如果您注销并以工作人员身份(但不是超级用户)查看,则只能看到public_field

测试案例2:重新启动服务器,如果您先以工作人员身份登录,您仍然只能看到public_field。但是,如果您以超级用户身份注销并查看,则请参阅secret_field。这是100%可重复的。

我做了一些基本的线程安全诊断:

  1. get_form()的末尾,我打印出了ModelForm对象的内存地址。应该是这样,每个请求都是唯一的。因此,ModelForm对象不是问题。
  2. 在管理员注册之前,我尝试打印ModelAdmin对象的内存地址。在测试用例1中,它对两个请求都是唯一的。但是对于测试用例2,它在第二次请求时根本不打印。
  3. 此时,我很无能为力。我的下一个研究点是管理员注册系统(我承认一无所知)。状态会在服务器重启时重置,因此似乎必须缓存ModelAdmin?还是线程安全问题?如果我把它变成工厂并返回ModelAdmin的deepcopy(),它会为每个请求提供一个新的ModelAdmin吗?我很无能为力,也很感激任何想法。谢谢!

1 个答案:

答案 0 :(得分:1)

我很困惑你为什么认为ModelAdmin应该是每个请求的新实例。管理对象由每个admin.py中的admin.site.register(Model)调用实例化,而这些调用又在urls.py中从admin.autodiscover()调用。换句话说,这发生在流程启动时。鉴于大多数Web服务环境的动态多进程特性,您可能会或可能不会获得任何特定请求的新流程 - 当然,您不会每次都获得一个。

因此,在ModelAdmin等全局对象上存储或更改状态是不明智的。我没有正确查看您的链接代码,但至少有一种情况是由于方法调用而在self上更改属性。不要那样做 - 你需要找到一些在方法之间传递动态值的方法。