2010-04-10 45 views
22

我想在这里会讨论类似的问题,但我找不到它。如何根据用户限制django-admin中的字段?

假设我有一位编辑和一位主管。我希望编辑能够添加新内容(例如新闻稿),但在发布之前必须由主管确认。

当编辑器列出所有项目时,我想将模型上的某些字段(如'ack'字段)设置为只读(所以他可以知道什么已经被删除,哪些还在等待批准),但是主管应该能够改变一切(list_editable将是完美

这个问题可能的解决方案是什么?

回答

15

我觉得还有一个更简单的方法来做到这一点:

客户,我们有博客的同样的问题 - 后

博客/ models.py:

Class Blog(models.Model): 
    ... 
    #fields like autor, title, stuff.. 
    ... 

class Post(models.Model): 
    ... 
    #fields like blog, title, stuff.. 
    ... 
    approved = models.BooleanField(default=False) 
    approved_by = models.ForeignKey(User) 
    class Meta: 
     permissions = (
      ("can_approve_post", "Can approve post"), 
     ) 

而且神奇的是在管理:

博客/ admin.py:

... 
from django.views.decorators.csrf import csrf_protect 
... 
def has_approval_permission(request, obj=None): 
    if request.user.has_perm('blog.can_approve_post'): 
     return True 
    return False 

Class PostAdmin(admin.ModelAdmin): 
    @csrf_protect 
    def changelist_view(self, request, extra_context=None): 
     if not has_approval_permission(request): 
      self.list_display = [...] # list of fields to show if user can't approve the post 
      self.editable = [...] 
     else: 
      self.list_display = [...] # list of fields to show if user can approve the post 
     return super(PostAdmin, self).changelist_view(request, extra_context) 
    def get_form(self, request, obj=None, **kwargs): 
     if not has_approval_permission(request, obj): 
      self.fields = [...] # same thing 
     else: 
      self.fields = ['approved'] 
     return super(PostAdmin, self).get_form(request, obj, **kwargs) 

通过这种方式,您可以在django中使用custom permission的api,并且您可以覆盖保存模型或获取查询集的方法(如果必须的话)。在方法has_approval_permission中,您可以定义用户何时可以或不可以做什么的逻辑。

+0

你可能是指 self.exclude = [ '批准']在get_form (),也有在changelist_view(小毛刺) ;) 谢谢,这看起来不错,与T.位组合Stone的回答,这正是我一直在寻找的东西:) – minder 2010-04-11 08:59:16

+0

如果我有'对象没有属性'COOKIES'呢? – andi 2014-02-13 12:18:05

+0

应该将权限注册到其他地方,以便在管理模块中可见? – andi 2014-02-13 12:21:18

2

我有一个这样的系统,就像我刚完成的一个项目一样。将有大量的工作放在了一起,但这里有一些,我不得不让我的系统工作的组成部分:

  • 您需要一种方法来定义一个编辑器和监事。这可以通过以下三种方式完成:1.通过具有定义管理程序的M2M字段[并假定每个有读/写权限的人都是编辑器]; 2.)创建2个从用户继承的新用户模型[可能比需要更多的工作]或者3.)使用django.auth能力来创建一个UserProfile类。方法#1可能是最合理的。

  • 一旦你能确定用户是什么类型,你需要一种通用的方式来执行你正在寻找的授权。我认为这里最好的路线可能是一个通用的管理模式。

  • 最后你需要某种类型的“父”的模式,将持有的任何需要审核的权限。例如,如果您有Blog模型和BlogPost模型(假设同一网站中有多个博客),那么Blog就是父模型(它可以拥有谁批准什么的权限)。但是,如果您有一个博客,并且BlogPost没有父模型,那么我们需要一些地方来存储权限。我发现ContentType在这里工作得很好。

下面是代码中的一些想法(未经测试,比实际更概念)。

制作一个名为'moderated'的新应用程序,它将保存我们的通用内容。

moderated.models.py

class ModeratedModelParent(models.Model): 
    """Class to govern rules for a given model""" 
    content_type = models.OneToOneField(ContentType) 
    can_approve = models.ManyToManyField(User) 

class ModeratedModel(models.Model): 
    """Class to implement a model that is moderated by a supervisor""" 
    is_approved = models.BooleanField(default=False) 

    def get_parent_instance(self): 
     """ 
     If the model already has a parent, override to return the parent's type 
     For example, for a BlogPost model it could return self.parent_blog 
     """ 

     # Get self's ContentType then return ModeratedModelParent for that type 
     self_content_type = ContentType.objects.get_for_model(self) 
     try:    
      return ModeratedModelParent.objects.get(content_type=self_content_type) 
     except: 
      # Create it if it doesn't already exist... 
      return ModeratedModelParent.objects.create(content_type=self_content_type).save() 

    class Meta: 
     abstract = True 

所以现在我们应该有代码一个通用的,可重复使用的位,我们可以找出许可给定的模型(我们将识别模型由它的内容类型)。

接下来,我们可以在管理再通过一个通用模型实现我们的政策:

moderated.admin.py

class ModeratedModelAdmin(admin.ModelAdmin): 

    # Save our request object for later 
    def __call__(self, request, url): 
     self.request = request 
     return super(ModeratedModelAdmin, self).__call__(request, url) 

    # Adjust our 'is_approved' widget based on the parent permissions 
    def formfield_for_dbfield(self, db_field, **kwargs): 
     if db_field.name == 'is_approved': 
      if not self.request.user in self.get_parent_instance().can_approve.all(): 
       kwargs['widget'] = forms.CheckboxInput(attrs={ 'disabled':'disabled' }) 

    # Enforce our "unapproved" policy on saves 
    def save_model(self, *args, **kwargs): 
     if not self.request.user in self.get_parent_instance().can_approve.all(): 
      self.is_approved = False 
     return super(ModeratedModelAdmin, self).save_model(*args, **kwargs) 

一旦这些设置和工作,我们可以重新像我发现的那样,在许多模型中使用它们,一旦你为这样的事情添加结构化权限,你很容易就想要它用于许多其他事情。例如你有一个新闻模型,你只需要将它从我们刚才创建的模型中继承下来,而且你很好。

# in your app's models.py 
class NewsItem(ModeratedModel): 
    title = models.CharField(max_length=200) 
    text = models.TextField() 


# in your app's admin.py 
class NewsItemAdmin(ModeratedModelAdmin): 
    pass 

admin.site.register(NewsItem, NewsItemAdmin) 

我敢肯定,我在那里做了一些代码错误和失误,但希望这可以给你一些想法充当无论你决定实施的跳板。

你必须做的最后一件事情,我将留给你,是实现对is_approved项目的过滤。 (即你不想被列在新闻部分未获得批准的项目,对吧?)

2

使用@ diegueus9概述的方法的问题是ModelAdmin行为喜欢单身,并且对于每个请求,未实例化为。这意味着每个请求都会修改其他请求正在访问的同一个ModelAdmin对象,这并不理想。下面是@ diegueus9所提出的解决方案:

# For example, get_form() modifies the single PostAdmin's fields on each request 
... 
class PostAdmin(ModelAdmin): 
    def get_form(self, request, obj=None, **kwargs): 
     if not has_approval_permission(request, obj): 
      self.fields = [...] # list of fields to show if user can't approve the post 
     else: 
      self.fields = ['approved', ...] # add 'approved' to the list of fields if the user can approve the post 
... 

另一种方法是通过fields作为关键字ARG给母公司get_form()方法,像这样:

... 
from django.contrib.admin.util import flatten_fieldsets 

class PostAdmin(ModelAdmin): 
    def get_form(self, request, obj=None, **kwargs): 
     if has_approval_permission(request, obj): 
      fields = ['approved'] 
      if self.declared_fieldsets: 
       fields += flatten_fieldsets(self.declared_fieldsets) 

      # Update the keyword args as needed to allow the parent to build 
      # and return the ModelForm instance you require for the user given their perms 
      kwargs.update({'fields': fields}) 
     return super(PostAdmin, self).get_form(request, obj=None, **kwargs) 
... 

这样,你是不是修改每个请求上的PostAdmin单例;您只需传递构建父代所需的适当关键字参数并返回ModelForm即可。

这可能是值得看get_form()方法的基础的ModelAdmin更多信息:https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L431

2

开始Django的1.7,现在就可以使用get_fields挂钩,这使得它这么多容易实现有条件的领域。

class MyModelAdmin(admin.ModelAdmin): 
    ... 

    def get_fields(self, request, obj=None): 
     fields = super(MyModelAdmin, self).get_fields(request, obj) 
     if request.user.is_superuser: 
      fields += ('approve',) 

     return fields 
+0

伟大的技术,但'fields = self.fields'导致我没有'None'。我将这行更改为'fields = super(MyModelAdmin,self).get_fields(request,obj)',它的功能就像一个魅力一样。 – PaulR 2017-06-21 14:28:45

+0

好的。我更新了我的答案。 – mixxorz 2017-06-21 15:58:02