Django 관리 인터페이스의 읽기 전용 모델?


86

관리자 인터페이스에서 모델을 완전히 읽기 전용으로 만들려면 어떻게해야합니까? 관리 기능을 사용하여 검색, 정렬, 필터링 등을 수행하고 있지만 로그를 수정할 필요가없는 일종의 로그 테이블 용입니다.

경우이 모습은 여기, 중복을 좋아 하지 내가 할 노력하고있어 :

  • 읽기 전용 필드를 찾고 있지 않습니다 (모든 필드를 읽기 전용으로 설정해도 새 레코드를 만들 수 있음).
  • 읽기 전용 사용자 를 만들려는 것이 아닙니다 . 모든 사용자는 읽기 전용이어야합니다.

2
이 기능은 곧해야합니다 github.com/django/django/pull/5297
보스코

2
has_view_permissionDjango 2.1에서 마침내 구현되었습니다. 아래의 stackoverflow.com/a/51641149 도 참조하십시오 .
djvg

답변:


21

참조 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

templates / admin / 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 %}

합법적 인 것 같습니다. 장고를 사용한 지 너무 오래 되었으니 다른 댓글 작성자의 의견을 기다릴 수도 있습니다.
Steve Bennett

이것은 Model, 또는에 대한 믹스 인입 ModelAdmin니까?
OrangeDog

그것은 ModelAdmin.
Pascal Polleunus

Django 1.8 이상에서는 get_all_field_names가 더 이상 사용되지 않습니다. 이전 버전과 호환되는 방법으로 가져옵니다 . 그들을 얻는 짧은 방법 .
fzzylogic

has_add_permission을 사용할 수 있습니다
rluts

70

관리자는보기뿐만 아니라 편집 용입니다 ( "보기"권한은 찾을 수 없음). 원하는 것을 얻으려면 추가, 삭제를 금지하고 모든 필드를 읽기 전용으로 설정해야합니다.

class MyAdmin(ModelAdmin):

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

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

(변경을 금지하면 물체도 볼 수 없습니다)

모든 필드를 읽기 전용으로 설정하는 것을 자동화하려는 테스트되지 않은 코드에 대해서는 내 대답을 참조하십시오. 전체 모델에

편집 : 또한 테스트되지 않았지만 LogEntryAdmin을 보았고

readonly_fields = MyModel._meta.get_all_field_names()

그것이 모든 경우에 작동하는지 모르겠습니다.

편집 : QuerySet.delete ()는 여전히 개체를 대량 삭제할 수 있습니다. 이 문제를 해결하려면 자신의 "객체"관리자와 삭제되지 않는 해당 QuerySet 하위 클래스를 제공하십시오 . Django에서 QuerySet.delete () 재정의를 참조하십시오.


2
추신 : 그리고 예, 다른 답변과 마찬가지로 갈 길은 아마도 ReadOnlyAdmin 클래스에서이 세 가지를 정의한 다음 해당 동작이 필요한 곳에서 하위 클래스를 정의하는 것입니다. 심지어 공상를 얻을 수 및 그룹 / 권한의 정의 허용 할 수 있습니다 (요청에 액세스 할 수 있으므로 현재 사용자를 가지고있는 () 및 사용 get_readonly_fields) 편집 허용을하고 상응 사실을 반환합니다.
Danny W. Adair 2011

거의 완벽합니다. 행이 편집 페이지에 연결되지 않는 방법이 있는지 탐욕스럽게 물어볼 수 있습니까? (다시, 어떤 행의 확대 필요, 편집 아무것도 할 필요가 없습니다)
스티브 베넷

1
ModelAdmin의 list_display_links를 False로 평가하는 항목 (예 : 빈 목록 / 튜플)으로 설정하면 ModelAdmin .__ init __ ()는 list_display_links를 모든 열 (작업 확인란 제외)로 설정합니다. options.py를 참조하십시오. 링크 가 있는지 확인 하기 위해 완료된 것 같습니다. 따라서 ReadOnlyAdmin에서 __init __ ()를 재정의하고 부모를 호출 한 다음 list_display_links를 빈 목록이나 튜플로 설정합니다. 이제 읽기 전용 변경 양식에 대한 링크가 없다는 점을 감안할 때 매개 변수 / 클래스 속성을 만드는 것이 가장 좋습니다. 일반적으로 원하는 동작이라고 생각하지 않습니다. Hth
Danny W. Adair 2011

모델에서 설정된 readonly_fields와 관련하여 양식을 재정의하고 다른 필드를 추가하면 작동하지 않을 수 있습니다. 실제 양식 필드를 기반으로하는 것이 더 좋습니다.
Danny W. Adair 2011

데프 __init __ (자기 * 인수) :이 작동하지 않았다 슈퍼 (RegistrationStatusAdmin, 자기를) .__ 초기화 __ (* 인수) self.display_links = []
스티브 베넷

50

다음은 모델을 만드는 데 사용하는 두 가지 클래스 및 / 또는 인라인 읽기 전용입니다.

모델 관리자 :

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

두 클래스를 하나의 하위 클래스에 적용하는 방법. 예를 들어 클래스에 일반 필드와 인라인이있는 경우? 둘 다 연장 할 수 있습니까?
Timo

@timo는이 클래스
MartinM

1
has_add_permissionin ReadOnlyAdmin은 요청 만 매개 변수로
받습니다.

has_change_permission ()도 재정의해야합니다. 데프 has_change_permission (자기, 요청, OBJ = 없음) :
데이비드 오일러

13

사용자가 편집 할 수 없다는 사실을 알리려면 첫 번째 솔루션에서 두 가지가 누락되었습니다. 삭제 작업을 제거했습니다!

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

둘째 : 읽기 전용 솔루션은 일반 모델에서 잘 작동합니다. 하지만 그렇지 않습니다 외래 키가있는 상속 된 모델이있는 경우 작동 . 불행히도 아직 그에 대한 해결책을 모릅니다. 좋은 시도는 다음과 같습니다.

읽기 전용으로 전체 모델

그러나 그것은 나에게도 효과가 없습니다.

마지막으로, 광범위한 솔루션을 생각하려면 각 인라인도 읽기 전용이어야합니다.


11

실제로 다음과 같은 간단한 해결책을 시도해 볼 수 있습니다.

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() False를 반환하면 해당 모델에 대한 새 개체가 생성되지 않습니다.

1
이것은 필드를보기 위해 인스턴스를 여는 것을 금지하지만 나열 만해도 괜찮다면 작동합니다.
Sebastián Vansteenkiste

8

이것은 8/1/18에 출시 된 Django 2.1에 추가되었습니다!

ModelAdmin.has_view_permission()기존 has_delete_permission, has_change_permission 및 has_add_permission과 같습니다. 문서에서 읽을 수 있습니다.여기

릴리스 정보에서 :

이를 통해 사용자에게 관리자의 모델에 대한 읽기 전용 액세스 권한을 부여 할 수 있습니다. ModelAdmin.has_view_permission ()은 새로운 기능입니다. 구현은 "변경"권한이있는 사용자가 개체를 편집 할 수 있도록 "보기"권한을 할당 할 필요가 없다는 점에서 이전 버전과 호환됩니다.


수퍼 유저는 여전히 관리 인터페이스에서 개체를 수정할 수 있습니다. 그렇죠?
Flimm

수퍼 유저의 액세스를 허용하지 않도록 동작을 변경하기 위해 이러한 방법 중 하나를 재정의하지 않는 한 맞습니다.
grrrrrr

6

수락 된 답변이 작동하지 않으면 다음을 시도하십시오.

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

5

@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에서만 이것을 시도했습니다.


Django를 사용한 지 아주 오래되었습니다. 다른 사람이 이것을 보증 할 수 있습니까?
Steve Bennett

@SteveBennett ㄹ이 주소 ...이 대답은 방수가 아니라는 요구 사항에 대한 변화의 많은 ...이 여기에 설명을 제안한다 stackoverflow.com/a/36019597/2586761 당신이에 댓글을 답 stackoverflow.com / A / 2,586,761분의 33,543,817 허용 대답보다 더 완벽한로
ptim

3

수락 된 대답은 작동하지만 읽기 전용 필드의 표시 순서도 유지됩니다. 또한이 솔루션으로 모델을 하드 코딩 할 필요가 없습니다.

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

3

Django 2.2에서는 다음과 같이합니다.

@admin.register(MyModel)
class MyAdmin(admin.ModelAdmin):
    readonly_fields = ('all', 'the', 'necessary', 'fields')
    actions = None # Removes the default delete action in list view

    def has_add_permission(self, request):
        return False

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

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

장고 2.2에서는 readonly_fieldsactions라인이 필요하지 않습니다
cheng10 dec

3

django 2.2에서는 읽기 전용 관리자가 다음과 같이 간단 할 수 있습니다.

class ReadOnlyAdminMixin():
    def has_add_permission(self, request):
        return False

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

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


class LogEntryAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
    list_display = ('id', 'user', 'action_flag', 'content_type', 'object_repr')

1

django 관리자의 특정 사용자에 대해 모든 필드를 읽기 전용으로 만들어야 할 때 동일한 요구 사항이 발생하여 내 코드를 롤링하지 않고 django 모듈 "django-admin-view-permission"을 활용하게되었습니다. 어떤 필드를 명시 적으로 정의하기 위해 더 세밀한 제어가 필요한 경우 모듈을 확장해야합니다. 여기 에서 작동중인 플러그인을 확인할 수 있습니다.


0

읽기 전용 =>보기 권한

  1. pipenv install django-admin-view-permission
  2. settings.py의 INSTALLED_APPS에 'admin_view_permission'을 다음과 같이 추가합니다.`INSTALLED_APPS = [ 'admin_view_permission',
  3. python manage.py 마이그레이션
  4. 파이썬 manage.py runserver 6666

ok. 'views'권한으로 즐기세요


0

인라인을 포함하여 사용자 권한에 따라 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, ... )
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.