2010-04-10 36 views
22

Tôi cho rằng vấn đề tương tự sẽ được thảo luận ở đây, nhưng tôi không thể tìm thấy nó.Làm cách nào để giới hạn các trường trong django-admin tùy thuộc vào người dùng?

Giả sử tôi có Trình chỉnh sửa và Người giám sát. Tôi muốn biên tập viên có thể thêm nội dung mới (ví dụ: một bài viết tin tức) nhưng trước khi xuất bản nó phải được giám sát viên thừa nhận. Khi biên tập liệt kê tất cả các mục, tôi muốn đặt một số trường trên các mô hình (như trường 'ack') là chỉ đọc (vì vậy anh ta có thể biết những gì đã được chỉnh sửa và những gì vẫn đang chờ phê duyệt) nhưng Người giám sát có thể thay đổi mọi thứ (list_editable sẽ là hoàn hảo)

Các giải pháp có thể cho vấn đề này là gì?

Trả lời

15

Tôi nghĩ rằng có một cách dễ dàng hơn để làm điều đó:

Khách chúng tôi có cùng một vấn đề của Blog -Post

blog/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"), 
     ) 

Và sự kỳ diệu là trong admin:

blog/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) 

Bằng cách này, bạn có thể sử dụng api của custom permission trong django và bạn có thể ghi đè các phương pháp để lưu mô hình hoặc nhận bộ truy vấn nếu cần. Trong metid has_approval_permission, bạn có thể xác định logic khi người dùng có thể hoặc không thể làm điều gì đó.

+0

bạn có thể có nghĩa là self.exclude = [ 'đã được phê duyệt'] trong get_form() và cũng có chút trục trặc trong changelist_view() ;) Cảm ơn, điều này có vẻ tuyệt vời và kết hợp với bit từ T. Câu trả lời của Stone, đó là chính xác những gì tôi đã tìm kiếm :) – minder

+0

nếu tôi có 'đối tượng không có thuộc tính 'COOKIES'' thì sao? – andi

+0

các quyền có được đăng ký ở mọi nơi khác để hiển thị trong mô-đun quản trị không? – andi

2

Tôi có loại hệ thống như thế này trên một dự án mà tôi vừa mới hoàn thành. Sẽ có rất nhiều công việc để kết hợp với nhau, nhưng đây là một số thành phần mà tôi phải làm cho hệ thống của mình hoạt động:

  • Bạn cần một cách để xác định Trình chỉnh sửa và Giám sát. Ba cách này có thể được thực hiện là 1.) bằng cách có một trường M2M xác định Giám sát [và giả định rằng mọi người khác có quyền đọc/ghi là một Editor], 2.) tạo 2 mô hình Người dùng mới kế thừa từ Người dùng [ có lẽ nhiều việc hơn cần thiết] hoặc 3.) sử dụng khả năng django.auth để có một lớp UserProfile. Phương pháp số 1 có lẽ là hợp lý nhất.

  • Khi bạn có thể xác định loại người dùng là gì, bạn cần một cách để thực thi chung ủy quyền mà bạn đang tìm kiếm. Tôi nghĩ tuyến đường tốt nhất ở đây có lẽ là mô hình quản trị chung.

  • Cuối cùng, bạn sẽ cần một số kiểu "cha mẹ" sẽ giữ quyền cho bất kỳ điều gì cần được kiểm duyệt. Ví dụ, nếu bạn có một mô hình Blog và mô hình BlogPost (giả sử nhiều blog trong cùng một trang), thì Blog là mô hình gốc (nó có thể giữ quyền của người phê duyệt cái gì). Tuy nhiên, nếu bạn có một blog duy nhất và không có mô hình gốc cho BlogPost, chúng tôi sẽ cần một số nơi để lưu trữ quyền. Tôi đã tìm thấy các ContentType hoạt động tốt ở đây.

Dưới đây là một số ý tưởng về mã (chưa được kiểm chứng và mang tính khái niệm hơn thực tế).

Tạo một ứng dụng mới có tên 'được kiểm duyệt' sẽ giữ nội dung chung của chúng tôi.

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 

Vì vậy, bây giờ chúng ta nên có một, tái sử dụng được chút generic mã mà chúng ta có thể xác định được phép cho một mô hình nhất định (mà chúng tôi sẽ xác định mô hình theo đó là Loại nội dung).

Tiếp theo, chúng ta có thể thực hiện chính sách của chúng tôi trong quản trị, một lần nữa thông qua một mô hình chung:

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) 

Khi đây là những thiết lập và làm việc, chúng ta có thể tái sử dụng chúng trên nhiều mô hình như tôi đã tìm thấy một khi bạn thêm quyền cấu trúc cho một cái gì đó như thế này, bạn dễ dàng muốn nó cho nhiều thứ khác.

Nói ví dụ bạn có một mô hình tin tức, bạn chỉ cần làm cho nó kế thừa từ mô hình mà chúng tôi vừa tạo và bạn tốt.

# 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) 

Tôi chắc chắn điều này có thể cung cấp cho bạn một số ý tưởng đóng vai trò là bệ phóng cho bất kỳ điều gì bạn quyết định triển khai.

Điều cuối cùng bạn phải làm, mà tôi sẽ để lại cho bạn, là triển khai lọc cho các mục is_approved. (. Tức là bạn không muốn các mặt hàng không được chấp thuận niêm yết trên phần tin tức, phải không?)

2

Sự cố khi sử dụng phương pháp được vạch ra bởi @ diegueus9 là hành vi của ModelAdmin thích một singleton và là không instanced cho mỗi yêu cầu. Điều này có nghĩa là mỗi yêu cầu đang sửa đổi cùng một đối tượng ModelAdmin đang được truy cập bởi các yêu cầu khác, điều này không lý tưởng. Dưới đây là những giải pháp được đề xuất bởi @ 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 
... 

Một cách tiếp cận thay thế sẽ được vượt qua fields như một arg từ khóa với phương pháp của cha mẹ get_form() như vậy:

... 
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) 
... 

Bằng cách này, bạn không được sửa đổi singleton PostAdmin theo mọi yêu cầu; bạn chỉ đơn giản là chuyển từ khóa thích hợp args cần thiết để xây dựng và trả về mô hình từ cha mẹ.

Nó có lẽ là giá trị nhìn vào get_form() phương pháp trên ModelAdmin cơ sở để biết thêm: https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L431

2

Bắt đầu Django 1,7, bây giờ bạn có thể sử dụng móc get_fields mà làm cho nó rất đơn giản hơn nhiều để thực hiện các lĩnh vực có điều kiện.

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

Kỹ thuật tuyệt vời nhưng 'trường = self.fields' dẫn đến' Không có' cho tôi.Tôi đã thay đổi dòng đó thành 'fields = super (MyModelAdmin, self) .get_fields (request, obj)' và nó hoạt động như một nét duyên dáng. – PaulR

+0

Rất thú vị. Tôi đã cập nhật câu trả lời của mình. – mixxorz

Các vấn đề liên quan