Django Admin - 特定用户(admin)内容

时间:2011-06-10 19:08:16

标签: django permissions django-admin admin

我开始组织一个新项目,让我说我会有一些模型,如产品目录

我将允许我的客户(不是访客,只有特定客户)登录Django Admin网站来创建,编辑和删除他们自己的目录。

假设我创建了一个名为“Shop”的模型,创建每个商店(名称,地址,徽标,联系信息等)并创建一个绑定到该商店的管理员用户。

现在我想要这个新的管理员(不是网站管理员,而是商店管理员 - 可能是用户组)来查看和编辑仅与他的商店关联的目录

这可能吗?

我应该在Django Admin中执行此操作,还是应该创建一个新的“shop admin”应用程序?

4 个答案:

答案 0 :(得分:58)

首先,提示警告:Django管理员设计理念是,任何有权访问管理员(is_staff==True)的用户都是可信赖的用户,例如:员工,因此“员工”指定甚至可以访问管理员。虽然您可以自定义管理员以限制区域,但允许组织内的任何人访问您的管理员都被认为是有风险的,而且Django在此时不保证任何类型的安全性。

现在,如果您仍想继续,您可以通过简单地不将这些权限分配给用户来限制除商店之外的所有商品。您必须让所有店主有权编辑他们需要访问的任何商店模型,但其他所有商品都应该不在其权限列表中。

然后,对于需要仅限于所有者眼睛的每个模型,您需要添加一个字段来存储“所有者”,或者允许用户访问它。您可以使用save_model ModelAdmin方法执行此操作,该方法可以访问请求对象:

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的易受攻击的视图以重定向用户不是所有者:

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()即可。这很好地抽象了模型类名,使代码不那么脆弱。我还更改为使用filterexists而不是使用try...except的{​​{1}}块。这种方式更简化,实际上也可以实现更简单的查询。

答案 1 :(得分:11)

我只是在这里发帖,因为最高评论不再是最新的答案。我正在使用Django 1.9,我不确定这是什么时候发生了变化。

例如,您有不同的场地和不同的用户与每个场地相关联,模型将看起来像这样:

class Venue(models.Model):
    user = models.ForeignKey(User)
    venue_name = models.CharField(max_length=255)
    area = models.CharField(max_length=255)

现在,如果用户允许通过django管理面板登录,则该用户的工作人员状态必须为true。

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允许我们通过'__'

访问不同类型的关系,这些关系可以达到我们想要的深度

Here's指向上述官方文档的链接。

答案 2 :(得分:0)

对不起,我知道已经晚了,但也许对其他人有帮助。我想source应用可以帮助实现目的。

答案 3 :(得分:0)

我认为RelatedOnlyFieldListFilter可以帮到你。 这里是Django Doc的链接:RelatedOnlyFieldListFilter

  

list_filter 可以是:元组,其中第一个元素是字段   name和第二个元素是继承自的类   django.contrib.admin.FieldListFilter,例如:

class PersonAdmin(admin.ModelAdmin):
    list_filter = (
        ('is_staff', admin.BooleanFieldListFilter),
    )
     

您可以将相关模型的选择限制为对象   使用RelatedOnlyFieldListFilter参与该关系:( Vous   pouvez限制器les choix d'unmodèleliéauxobjetsincissésparla   relation en utilisant RelatedOnlyFieldListFilter:)

  class BookAdmin(admin.ModelAdmin):
      list_filter = (
           ('author', admin.RelatedOnlyFieldListFilter),
      ) 
     

假设作者是User模型的ForeignKey,这会将list_filter选项限制为已经写过书的用户   而不是列出所有用户。 (En supposant que author estuneclé   ForeignKey versunmodèleUser,cela va limiter les choix de   list_filter aux utilisateurs quiontécritunlivre au lieu d'énumérer   tous les utilisateurs。)