Как ограничить поля в django-администраторе в зависимости от пользователя?

Я предполагаю, что подобная проблема была бы обсуждена здесь, но я не мог найти его.

Давайте предположим, что у меня есть Редактор и Супервизор. Я хочу, чтобы Редактор смог добавить новое содержание (например, сообщение новостей), но перед публикацией оно должно быть подтверждено Супервизором.

Когда Редактор перечисляет все объекты, я хочу установить некоторые поля на моделях (как 'ack' поле) как только для чтения (таким образом, он мог знать то, что было ack'ed и что все еще ожидает одобрение), но Супервизор должен смочь измениться, все (list_editable было бы прекрасно),

Что возможные решения к этой проблеме?

27
задан minder 10 April 2010 в 21:49
поделиться

2 ответа

Я думаю, что есть более простой способ сделать это:

Гость, у нас та же проблема, что и в 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"),
         )

И волшебство в админке:

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)

Таким образом, вы можете использовать api пользовательского разрешения в django, и вы можете переопределить методы для сохранения модели или получения набора запросов, если вам нужно. В метиде has_approval_permission вы можете определить логику того, когда пользователь может или не может что-то делать.

19
ответ дан 28 November 2019 в 05:46
поделиться

У меня есть система, похожая на эту, в проекте, который я только что закончил. Будет много работы, чтобы собрать ее вместе, но вот некоторые из компонентов, которые я должен был сделать, чтобы моя система работала:

  • Вам нужен способ определить редактора и супервизора. Это можно сделать тремя способами: 1.) с помощью поля M2M, определяющего супервайзера [и предполагая, что все остальные, имеющие право читать/писать, являются редакторами], 2.) сделать 2 новые модели пользователей, которые наследуются от User [вероятно, больше работы, чем нужно] или 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
ответ дан 28 November 2019 в 05:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: