首先,需要注意的是:Django管理员设计哲学认为任何能够访问管理员页面(is_staff==True
)的用户都是受信任的用户,例如员工,因此即使获得访问管理员的权限也需要具备“员工”身份。虽然您可以定制管理员以限制区域,但允许组织外的任何人访问您的管理员被认为是有风险的,并且Django不保证在这一点上的任何安全性。
现在,如果您仍想继续,可以通过简单地不将那些权限分配给用户来限制大部分内容,但商店除外。您需要给所有商店所有者授予编辑它们需要访问的任何商店模型的权限,但应该在他们的权限列表中留下其他所有内容。
接下来,对于每个需要仅限所有者查看的模型,您需要添加一个字段来存储“所有者”或被允许访问它的用户。您可以通过 ModelAdmin
上的 save_model
方法实现这一点,该方法可以访问请求对象:
class MyModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super(MyModelAdmin, self).save_model(request, obj, form, change)
你还需要将ModelAdmin的queryset限制为仅属于当前用户的项目:
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyModelAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
然而,这仅仅会限制被列出的内容,用户仍可以通过更改URL访问他们没有权限访问的其他对象,因此您需要覆盖每个ModelAdmin的易受攻击视图,如果用户不是所有者,则重定向。
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
class MyModelAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, form_url='', extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).change_view(request, object_id, form_url, extra_context)
def delete_view(self, request, object_id, extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).delete_view(request, object_id, extra_context)
def history_view(self, request, object_id, extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).history_view(request, object_id, extra_context)
更新 06/05/12
感谢@christophe31指出,由于ModelAdmin
的查询集已经被限制在用户范围内,你只需要在更改、删除和历史视图中使用self.queryset()
。这样可以很好地抽象出模型类名,使代码更加健壮。我还改用了filter
和exists
,而不是try...except
块与get
。这种方式更为简洁,实际上也会导致一个更简单的查询。
change_view
需要四个参数,而extra_context
是可选的第四个参数。通过你的方式传递它,你会覆盖form_url
参数,这决定了管理页面上表单操作的行为。解决方案要么是捕获并传递表单URL,要么更加健壮/具有未来性,使用extra_context=extra_context
。 - jfmattqueryset
方法更改为get_queryset
。 - Ekin Ertaçis_staff=True
的最佳替代方案是什么?是否可以像这里建议的那样拥有第二个管理员站点来限制权限? - djvg