Подтвердить что ты не робот

Модели Readonly в интерфейсе администратора Django?

Как я могу сделать модель полностью доступной только для чтения в интерфейсе администратора? Это для своего рода таблицы журналов, где я использую функции администратора для поиска, сортировки, фильтрации и т.д., Но нет необходимости изменять журнал.

Если это похоже на дубликат, здесь не то, что я пытаюсь сделать:

  • Я не ищу поля readonly (даже делая каждое поле readonly все равно позволяющим создавать новые записи)
  • Я не хочу создавать пользователя только для чтения: каждый пользователь должен быть только для чтения.
4b9b3361

Ответ 1

Смотрите https://djangosnippets.org/snippets/10539/

class ReadOnlyAdminMixin(object):
    """Disables all editing capabilities."""
    change_form_template = "admin/view.html"

    def __init__(self, *args, **kwargs):
        super(ReadOnlyAdminMixin, self).__init__(*args, **kwargs)
        self.readonly_fields = self.model._meta.get_all_field_names()

    def get_actions(self, request):
        actions = super(ReadOnlyAdminMixin, self).get_actions(request)
        del_action = "delete_selected"
        if del_action in actions:
            del actions[del_action]
        return actions

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def save_model(self, request, obj, form, change):
        pass

    def delete_model(self, request, obj):
        pass

    def save_related(self, request, form, formsets, change):
        pass

шаблоны/администратор/view.html

{% extends "admin/change_form.html" %}
{% load i18n %}

{% block submit_buttons_bottom %}
  <div class="submit-row">
    <a href="../">{% blocktrans %}Back to list{% endblocktrans %}</a>
  </div>
{% endblock %}

templates/admin/view.html (для Grappelli)

{% extends "admin/change_form.html" %}
{% load i18n %}

{% block submit_buttons_bottom %}
  <footer class="grp-module grp-submit-row grp-fixed-footer">
    <header style="display:none"><h1>{% trans "submit options"|capfirst context "heading" %}</h1></header>
    <ul>
       <li><a href="../" class="grp-button grp-default">{% blocktrans %}Back to list{% endblocktrans %}</a></li>
    </ul>
  </footer>
{% endblock %}

Ответ 2

Администратор предназначен для редактирования, а не только для просмотра (вы не найдете разрешения "view" ). Чтобы добиться того, чего вы хотите, вам придется запретить добавлять, удалять и делать все поля только для чтения:

class MyAdmin(ModelAdmin):

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

(если вы запретите изменение, вы даже не увидите объекты)

Для некоторого непроверенного кода, который пытается автоматизировать установку всех полей только для чтения, см. мой ответ на Целая модель как доступная только для чтения

EDIT: также непроверенный, но просто посмотрел на мой LogEntryAdmin, и он имеет

readonly_fields = MyModel._meta.get_all_field_names()

Не знаю, будет ли это работать во всех случаях.

РЕДАКТИРОВАТЬ: QuerySet.delete() может по-прежнему массово удалять объекты. Чтобы обойти это, предоставить свой собственный менеджер "объектов" и соответствующий подкласс QuerySet, который не удаляет - см. Переопределение QuerySet.delete() в Django

Ответ 3

Вот два класса, которые я использую, чтобы сделать модель и/или она встроена только для чтения.

Для администратора модели:

from django.contrib import admin

class ReadOnlyAdmin(admin.ModelAdmin):
    readonly_fields = []

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
               [field.name for field in obj._meta.fields] + \
               [field.name for field in obj._meta.many_to_many]


    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

class MyModelAdmin(ReadOnlyAdmin):
    pass

Для строк:

class ReadOnlyTabularInline(admin.TabularInline):
    extra = 0
    can_delete = False
    editable_fields = []
    readonly_fields = []
    exclude = []

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
               [field.name for field in self.model._meta.fields
                if field.name not in self.editable_fields and
                   field.name not in self.exclude]

    def has_add_permission(self, request):
        return False


class MyInline(ReadOnlyTabularInline):
    pass

Ответ 4

Если вы хотите, чтобы пользователь осознал, что он/она не может его редактировать, на первом решении отсутствует 2 части. Вы удалили действие удаления!

class MyAdmin(ModelAdmin)
    def has_add_permission(self, request, obj=None):
        return False
    def has_delete_permission(self, request, obj=None):
        return False

    def get_actions(self, request):
        actions = super(MyAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

Второе: решение на основе readonly отлично работает на простых моделях. Но он работает НЕ, если у вас есть унаследованная модель с внешними ключами. К сожалению, я пока не знаю решения для этого. Хорошая попытка:

Целая модель доступна только для чтения

Но это тоже не работает для меня.

И последнее замечание, если вы хотите подумать о широком решении, вы должны обеспечить, чтобы каждая строка была также доступна только для чтения.

Ответ 5

На самом деле вы можете попробовать это простое решение:

class ReadOnlyModelAdmin(admin.ModelAdmin):
    actions = None
    list_display_links = None
    # more stuff here

    def has_add_permission(self, request):
        return False
  • actions = None: избегает отображения раскрывающегося списка с опцией "Удалить выбранный..."
  • list_display_links = None: избегает нажатия в столбцах для редактирования этого объекта
  • has_add_permission() return False позволяет создавать новые объекты для этой модели

Ответ 6

Это было добавлено в Django 2.1, который был выпущен 18 августа!

ModelAdmin.has_view_permission() точно так же, как существующие has_delete_permission, has_change_permission и has_add_permission. Вы можете прочитать об этом в документации здесь

Из заметок о выпуске:

Это позволяет предоставить пользователям доступ только для чтения к моделям в админке. ModelAdmin.has_view_permission() является новым. Реализация обратная совместимость в том, что нет необходимости назначать "представление" разрешение разрешать пользователям, имеющим разрешение на изменение, редактировать объекты.

Ответ 7

Если принятый ответ не работает для вас, попробуйте следующее:

def get_readonly_fields(self, request, obj=None):
    readonly_fields = []
    for field in self.model._meta.fields:
        readonly_fields.append(field.name)

    return readonly_fields

Ответ 8

Компиляция @darklow и @josir отличных ответов, плюс добавление немного больше, чтобы удалить кнопки "Сохранить" и "Сохранить и продолжить", приводит к (в синтаксисе Python 3):

class ReadOnlyAdmin(admin.ModelAdmin):
    """Provides a read-only view of a model in Django admin."""
    readonly_fields = []

    def change_view(self, request, object_id, extra_context=None):
        """ customize add/edit form to remove save / save and continue """
        extra_context = extra_context or {}
        extra_context['show_save_and_continue'] = False
        extra_context['show_save'] = False
        return super().change_view(request, object_id, extra_context=extra_context)

    def get_actions(self, request):
        actions = super().get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
           [field.name for field in obj._meta.fields] + \
           [field.name for field in obj._meta.many_to_many]

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

а затем вы используете как

class MyModelAdmin(ReadOnlyAdmin):
    pass

Я только пробовал это с Django 1.11/Python 3.

Ответ 9

Принятый ответ должен работать, но это также сохранит порядок отображения полей readonly. Вам также не нужно жестко кодировать модель с помощью этого решения.

class ReadonlyAdmin(admin.ModelAdmin):
   def __init__(self, model, admin_site):
      super(ReadonlyAdmin, self).__init__(model, admin_site)
      self.readonly_fields = [field.name for field in filter(lambda f: not f.auto_created, model._meta.fields)]

   def has_delete_permission(self, request, obj=None):
       return False
   def has_add_permission(self, request, obj=None):
       return False

Ответ 10

Я столкнулся с тем же требованием, когда нужно было сделать все поля для чтения только для определенных пользователей в django admin, что привело к использованию модуля django "django-admin-view-permission", не сворачивая мой собственный код. Если вам требуется более мелкозернистый элемент управления для явного определения полей, то вам нужно будет расширить модуль. Вы можете проверить плагин в действии здесь

Ответ 11

только для чтения => разрешение на просмотр

  1. pipenv install django-admin-view-permission
  2. добавьте admin_view_permission к INSTALLED_APPS в settings.py. вот так: 'INSTALLED_APPS = [ 'Admin_view_permission',
  3. python manage.py migrate
  4. python manage.py runserver 6666

хорошо. развлекайтесь с разрешениями "views"

Ответ 12

Я написал общий класс для обработки представления ReadOnly в зависимости от прав пользователя, в том числе встроенных;)

В models.py:

class User(AbstractUser):
    ...
    def is_readonly(self):
        if self.is_superuser:
            return False
        # make readonly all users not in "admins" group
        adminGroup = Group.objects.filter(name="admins")
        if adminGroup in self.groups.all():
            return False
        return True

В admin.py:

# read-only user filter class for ModelAdmin
class ReadOnlyAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        # keep initial readonly_fields defined in subclass
        self._init_readonly_fields = self.readonly_fields
        # keep also inline readonly_fields
        for inline in self.inlines:
            inline._init_readonly_fields = inline.readonly_fields
        super().__init__(*args,**kwargs)
    # customize change_view to disable edition to readonly_users
    def change_view( self, request, object_id, form_url='', extra_context=None ):
        context = extra_context or {}
        # find whether it is readonly or not 
        if request.user.is_readonly():
            # put all fields in readonly_field list
            self.readonly_fields = [ field.name for field in self.model._meta.get_fields() if not field.auto_created ]
            # readonly mode fer all inlines
            for inline in self.inlines:
                inline.readonly_fields = [field.name for field in inline.model._meta.get_fields() if not field.auto_created]
            # remove edition buttons
            self.save_on_top = False
            context['show_save'] = False
            context['show_save_and_continue'] = False
        else:
            # if not readonly user, reset initial readonly_fields
            self.readonly_fields = self._init_readonly_fields
            # same for inlines
            for inline in self.inlines:
                inline.readonly_fields = self._init_readonly_fields
        return super().change_view(
                    request, object_id, form_url, context )
    def save_model(self, request, obj, form, change):
        # disable saving model for readonly users
        # just in case we have a malicious user...
        if request.user.is_readonly():
            # si és usuari readonly no guardem canvis
            return False
        # if not readonly user, save model
        return super().save_model( request, obj, form, change )

Затем мы можем просто наследовать наши классы в admin.py:

class ContactAdmin(ReadOnlyAdmin):
    list_display = ("name","email","whatever")
    readonly_fields = ("updated","created")
    inlines = ( PhoneInline, ... )