我正在使用django-guardian来管理每个对象的权限。
对于给定的用户,我授予一个对象的所有权限:
joe = User.objects.get(username="joe")
mytask = Task.objects.get(pk=1)
assign('add_task', joe, mytask)
assign('change_task', joe, mytask)
assign('delete_task', joe, mytask)
我按预期得到了:
In [57]: joe.has_perm("add_task", mytask)
Out[57]: True
In [58]: joe.has_perm("change_task", mytask)
Out[58]: True
In [59]: joe.has_perm("delete_task", mytask)
Out[59]: True
在admin.py中我还使TaskAdmin继承自GuardedModelAdmin
而不是admin.ModelAdmin
现在当我用joe连接到我的网站时,我得到了管理员:
You don't have permission to edit anything
我不应该能够编辑对象mytask吗?
我是否必须使用内置的基于模型的权限系统设置一些权限?
我错过了什么吗?
修改
我尝试添加user_can_access_owned_objects_only
选项,该选项可以解决我的问题,但我的管理员仍然看不到任何内容......
class TaskAdmin(GuardedModelAdmin):
user_can_access_owned_objects_only = True
pass
admin.site.register(Task, TaskAdmin)
谢谢
答案 0 :(得分:3)
为了只查看当前用户拥有的实例,我授予他所有权限
add_task=Permission.objects.get(codename="add_task")
change_task=Permission.objects.get(codename="change_task")
delete_task=Permission.objects.get(codename="delete_task")
joe.user_permissions.add(add_task)
joe.user_permissions.add(change_task)
joe.user_permissions.add(delete_task)
然后我使用guardian.shortcuts.assign在几个实例上设置权限,并在admin中过滤查询集:
class TaskAdmin(admin.ModelAdmin):
def queryset(self, request):
if request.user.is_superuser:
return super(TaskAdmin, self).queryset(request)
return get_objects_for_user(user=request.user, perms=['add_task', 'change_task', 'delete_task'], klass=Task)
这远非完美,但我找不到任何其他解决方案。
答案 1 :(得分:2)
Django Admin,特别是它正在处理w / change
权限,有点粗糙。内部方法ModelAdmin.has_change_permission()
涵盖了view
权限的检查,这实际上是缺少的。
其次,GuardedModelAdmin
带来了所有权检查(通过user_can_access_owned_objects_only
)和用于管理行级权限的表单。它不向Django Admin提供任何其他行级访问策略。
对于Django Admin中典型的行级权限场景,我想建议代码,这里我引入了一个可选的“视图”权限:
class ExtendedGuardedModelAdmin(GuardedModelAdmin):
def queryset(self, request):
qs = super(ExtendedGuardedModelAdmin, self).queryset(request)
# Check global permission
if super(ExtendedGuardedModelAdmin, self).has_change_permission(request) \
or (not self.list_editable and self.has_view_permission(request)):
return qs
# No global, filter by row-level permissions. also use view permission if the changelist is not editable
if self.list_editable:
return get_objects_for_user(request.user, [self.opts.get_change_permission()], qs)
else:
return get_objects_for_user(request.user, [self.opts.get_change_permission(), self.get_view_permission(
)], qs, any_perm=True)
def has_change_permission(self, request, obj=None):
if super(ExtendedGuardedModelAdmin, self).has_change_permission(request, obj):
return True
if obj is None:
# Here check global 'view' permission or if there is any changeable items
return self.has_view_permission(request) or self.queryset(request).exists()
else:
# Row-level checking
return request.user.has_perm(self.opts.get_change_permission(), obj)
def get_view_permission(self):
return 'view_%s' % self.opts.object_name.lower()
def has_view_permission(self, request, obj=None):
return request.user.has_perm(self.opts.app_label + '.' + self.get_view_permission(), obj)
def has_delete_permission(self, request, obj=None):
return super(ExtendedGuardedModelAdmin, self).has_delete_permission(request, obj) \
or (obj is not None and request.user.has_perm(self.opts.get_delete_permission(), obj))
通过这种方式,您可以实现更灵活的权限检查,用户权限现在是全局的,用户对象权限是基于行级的:
joe.user_permissions.add(add_task)
joe.user_permissions.add(change_task)
joe.user_permissions.add(delete_task)
assign(Task._meta.get_change_permission(), joe, obj)
assign(Task._meta.get_delete_permission(), joe, obj)
assign('view_task', joe, obj)
joe.user_permissions.add(Permission.objects.get(codename='view_task', ...))
如果对你没用,你可以安全地忽略'查看'权限。
目前,django.contrib.admin.util.get_deleted_objects
在检查权限期间不尊重obj,如果您需要在删除期间检查行级权限,请通过修改get_deleted_objects
行来修补if not user.has_perm(p):
行if not user.has_perm(p, obj):
。相关门票为13539& 16862