Django的pipe理员 – 特定的用户(pipe理员)的内容
我开始组织一个新的项目,比方说,我会有几个模型,如产品和目录 。
我将允许我的客户(不是访问者,只有特定的客户端)在Django Admin站点上login来创build,编辑和删除他们自己的目录。
比方说,我创build一个名为“商店”的模型,创build每个商店(名称,地址,标志,联系信息等),并创build绑定到该商店的pipe理员用户。
现在我想要这个新的pipe理员(谁不是一个网站pipe理员,而是一个店铺pipe理员 – 可能是一个用户组 )只能查看和编辑与他的商店链接的目录 。
那可能吗?
我应该在Django Admin中做这个,还是应该创build一个新的“店铺pipe理员”应用程序?
首先,注意警告:Djangopipe理员的devise理念是任何is_staff==True
访问pipe理员( is_staff==True
)的用户都是可信的用户,例如员工,因此“员工”称号甚至可以访问pipe理员。 虽然您可以自定义pipe理员来限制区域,但是允许不在您的组织内的任何人访问您的pipe理员都被认为是有风险的,Django对此时的任何安全性都没有任何保证。
现在,如果您仍想继续操作,您可以通过简单地将这些权限分配给用户来限制绝大多数商店,但商店不受限制。 您必须让所有店主拥有编辑他们需要访问的任何商店模型的权利,但是其他所有内容都应该保留在他们的权限列表中。
然后,对于每个需要仅限于所有者眼睛的模型,您需要添加一个字段来存储“所有者”,或允许用户访问它。 您可以使用save_model
上的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的查询集限制为仅由当前用户拥有的项目:
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易受攻击的视图以redirect主人:
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
,而不是try...except
exists
, try...except
块。 这样更简化,实际上也会导致更简单的查询。
我只是在这里发表,因为最重要的评论不再是最新的答案。 我正在使用Django 1.9,我不知道这是什么时候发生了变化。
例如,您有不同的场地和与每个场地相关的不同用户,模型将如下所示:
class Venue(models.Model): user = models.ForeignKey(User) venue_name = models.CharField(max_length=255) area = models.CharField(max_length=255)
现在,如果用户允许通过djangopipe理面板login,则该用户的员工状态必须为真。
admin.py看起来像这样:
class FilterUserAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): if getattr(obj, 'user', None) is None: obj.user = request.user obj.save() def get_queryset(self, request): qs = super(FilterUserAdmin, self).queryset(request) if request.user.is_superuser: return qs return qs.filter(user=request.user) def has_change_permission(self, request, obj=None): if not obj: return True return obj.user == request.user or request.user.is_superuser @admin.register(Venue) class VenueAdmin(admin.ModelAdmin): pass
函数名称已从queryset更改为get_queryset。
编辑:我想扩大我的答案。 还有另一种方法可以在不使用queryset函数的情况下返回过滤对象。 我想强调,我不知道这种方法是更有效还是效率更低。
get_queryset方法的替代实现如下所示:
def get_queryset(self, request): if request.user.is_superuser: return Venue.objects.all() else: return Venue.objects.filter(user=request.user)
而且,我们也可以过滤内容,关系是更深的。
class VenueDetails(models.Model): venue = models.ForeignKey(Venue) details = models.TextField()
现在,如果我想过滤这个具有Venue作为外键但没有User的模型,我的查询更改如下:
def get_queryset(self, request): if request.user.is_superuser: return VenueDetails.objects.all() else: return VenueDetails.objects.filter(venue__user=request.user)
Django ORM允许我们通过'__'访问不同types的关系,这些关系可以像我们想要的一样深入。
以上是官方文档的链接。
我很抱歉,我知道已经晚了,但是也许会帮助其他人。 我猜django权限的应用程序可以帮助执行这个目的。
我认为RelatedOnlyFieldListFilter应该帮助你。 这里链接到Django Doc: RelatedOnlyFieldListFilter
list_filter可以是:一个元组,其中第一个元素是一个字段名,第二个元素是一个inheritance自django.contrib.admin.FieldListFilter的类,例如:
class PersonAdmin(admin.ModelAdmin): list_filter = ( ('is_staff', admin.BooleanFieldListFilter), )
您可以使用RelatedOnlyFieldListFilter 将相关模型的select限制为涉及该关系中的对象:(Vous pouvez limiter les choix d'unmodèleliéaux objetsconcernéspar la relation en utilisant RelatedOnlyFieldListFilter 🙂
class BookAdmin(admin.ModelAdmin): list_filter = ( ('author', admin.RelatedOnlyFieldListFilter), )
假设作者是一个用户模型的ForeignKey, 这将限制list_filter的select给写一本书的用户,而不是列出所有的用户 。 (En supposant que author est est unecléForeignKey vers unmodèleUser,cela va limiter les choix de list_filter aux utilisateurs quiontécritun livre au lieu d'énumérertous les utilisateurs。)