我想在這裏會討論類似的問題,但我找不到它。如何根據用戶限制django-admin中的字段?
假設我有一位編輯和一位主管。我希望編輯能夠添加新內容(例如新聞稿),但在發佈之前必須由主管確認。
當編輯器列出所有項目時,我想將模型上的某些字段(如'ack'字段)設置爲只讀(所以他可以知道什麼已經被刪除,哪些還在等待批准),但是主管應該能夠改變一切(list_editable將是完美)
這個問題可能的解決方案是什麼?
我想在這裏會討論類似的問題,但我找不到它。如何根據用戶限制django-admin中的字段?
假設我有一位編輯和一位主管。我希望編輯能夠添加新內容(例如新聞稿),但在發佈之前必須由主管確認。
當編輯器列出所有項目時,我想將模型上的某些字段(如'ack'字段)設置爲只讀(所以他可以知道什麼已經被刪除,哪些還在等待批准),但是主管應該能夠改變一切(list_editable將是完美)
這個問題可能的解決方案是什麼?
我覺得還有一個更簡單的方法來做到這一點:
客戶,我們有博客的同樣的問題 - 後
博客/ 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
中,您可以定義用戶何時可以或不可以做什麼的邏輯。
我有一個這樣的系統,就像我剛完成的一個項目一樣。將有大量的工作放在了一起,但這裏有一些,我不得不讓我的系統工作的組成部分:
您需要一種方法來定義一個編輯器和監事。這可以通過以下三種方式完成: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
項目的過濾。 (即你不想被列在新聞部分未獲得批准的項目,對吧?)
使用@ 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
開始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
你可能是指 self.exclude = [ '批准']在get_form (),也有在changelist_view(小毛刺) ;) 謝謝,這看起來不錯,與T.位組合Stone的回答,這正是我一直在尋找的東西:) – minder 2010-04-11 08:59:16
如果我有'對象沒有屬性'COOKIES'呢? – andi 2014-02-13 12:18:05
應該將權限註冊到其他地方,以便在管理模塊中可見? – andi 2014-02-13 12:21:18