理解Django管理界面的readonly_fields属性

7
我创建了一些代码来区分Django管理界面中的两个用户组,结果是显示所有字段为只读或仅显示在ModelAdmin类中直接设置的某些字段。
首先是这段代码:
class PersonAdmin(admin.ModelAdmin):
    readonly_fields = ('created_at','created_by',)

def get_form(self, request, obj=None, **kwargs):
    if obj:     # we are in edit mode
        if request.user.is_superuser:
            self.readonly_fields = ()
        else:
            for group in request.user.groups.all():
                if str(group) == 'readonlyuser':
                    allfields = tuple(obj._meta.get_all_field_names())
                    self.readonly_fields = allfields

    return super(PersonAdmin, self).get_form(request, obj, **kwargs)

我将分组并相应地设置字段。如果两个组的用户没有同时登录,则一切正常!但是,如果一个“只读”用户登录后,管理员用户也将获得所有只读字段。我的检查提供了一个解决方案:如果在for循环块中增加一个针对管理员用户的if语句,则一切都可以按预期工作。
if str(group) == 'adminuser':
    self.readonly_fields = PersonAdmin.readonly_fields

为什么会这样?发生了什么?

我没有设置特殊的缓存,它在开发服务器和安装WSGI的Apache上都会发生。

据我所知,request.user.groups.all()应该返回当前登录用户所属的所有组。如果不同IP和会话的其他用户匹配此if块,Django从哪里获取所有字段(只读)?

2个回答

16

ModelAdmin 只会在收到所有请求时被实例化一次。因此,当您像这样定义只读字段时,您正在永久地将其设置在整个板块中。

只要您使用 Django 1.2+,就可以使用 get_readonly_fields 方法来精确完成此操作:

class MyModelAdmin(admin.ModelAdmin):
    ...

    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return super(MyModelAdmin, self).get_readonly_fields(request, obj)
        else:
            return ('created_at', 'created_by')

从您的ModelAdmin中删除readonly_fields属性,或将其设置为对于所有人都应该是只读的字段。然后,在else块中指定所有应仅对非超级用户只读的字段。


谢谢,按照说明运行正常。我已经使用了get_readonly_fields,但是可能太糊涂了,没想到可以用它来实现这个目的。 - normic
那个例子没有通过obj参数,而是总是将其设置为None。应该是: return super(MyModelAdmin, self).get_readonly_fields(request, obj) - seddonym
@seddonym:发现得好。现在已经修复了。只用了一年半时间才有人注意到 ;) - Chris Pratt

1

因为在重新启动Web服务器后,Django管理界面中的字段被设置(即缓存)后第一次运行。您可以通过重新声明readonly_fields元组来解决这个问题。类似于以下内容(未经测试):

def get_form(self, request, obj=None, **kwargs):  
    self.readonly_fields = ('created_at','created_by',)
    # ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接