理解Django admin的readonly_fields
我写了一些代码,用来区分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
这是为什么呢?发生了什么事?
我没有特别的缓存设置,这个问题在开发服务器和使用Apache与WSGI的服务器上都出现。
根据我的理解,request.user.groups.all()应该返回当前登录用户所属的所有组。如果另一个用户在不同的IP和会话下匹配了这个if语句,Django是从哪里获取所有字段(只读)的呢?
2 个回答
1
因为在Django管理后台中,字段是在第一次运行时(也就是当网页服务器重启后)被设置好的(也就是被缓存起来了)。你可以通过重新声明readonly_fields这个元组来解决这个问题。可以试试下面这样的写法(这个没有测试过):
def get_form(self, request, obj=None, **kwargs):
self.readonly_fields = ('created_at','created_by',)
# ...
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')
把 readonly_fields
属性从你的 ModelAdmin
中去掉,或者设置成对所有人都应该是只读的字段。然后,在 else
这个部分里,指定那些只对非超级用户是只读的字段。