Django管理インターフェースの読み取り専用モデル?


86

管理インターフェースでモデルを完全に読み取り専用にするにはどうすればよいですか?これは一種のログテーブル用で、管理機能を使用して検索、並べ替え、フィルタリングなどを行っていますが、ログを変更する必要はありません。

場合には、このルックスはここだ、重複が好きでない私が何をしようとしています:

  • 私は読み取り専用フィールドを探していません(すべてのフィールドを読み取り専用にしても、新しいレコードを作成できます)
  • 読み取り専用ユーザーを作成するつもりはありません。すべてのユーザーは読み取り専用である必要があります。

2
この機能はまもなく登場する予定です:github.com/django/django/pull/5297
Bosco

2
has_view_permission最終的にDjango2.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

テンプレート/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 %}

スジは通ってるようだ。私がDjangoを使用してからかなりの時間が経ちましたが、他のコメント投稿者の発言を待つかもしれません。
スティーブベネット

これはModel、またはのミックスインModelAdminですか?
OrangeDog 2018年

のためModelAdminです。
パスカルポレウヌス2018年

Django 1.8以降では、get_all_field_namesは非推奨になりました。それらを取得するための下位互換性のある方法それらを取得する短い方法
fzzylogic 2018

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
PS:はい、他の回答と同様に、おそらく、ReadOnlyAdminクラスでこれら3つのことを定義し、その動作が必要な場所からサブクラス化することです。ファンシーになり、編集許可されているグループ/パーミッションの定義を許可し、それに応じてTrueを返すこともできます(リクエストにアクセスできるget_readonly_fields()を使用して、現在のユーザーを使用します)。
ダニーW.アデア2011年

ほぼ完璧。行を編集ページにリンクさせない方法があるかどうかを貪欲に尋ねることはできますか?(ここでも、行を拡大する必要はなく、何も編集する必要はありません)
Steve Bennett

1
ModelAdminのlist_display_linksをFalseと評価されるもの(空のリスト/タプルなど)に設定すると、ModelAdmin .__ init __()はlist_display_linksをすべての列(アクションチェックボックスを除く)に設定します-options.pyを参照してください。リンクがあることを確認するために行われたと思います。したがって、ReadOnlyAdminで__init __()をオーバーライドし、親を呼び出してから、list_display_linksを空のリストまたはタプルに設定します。読み取り専用の変更フォームへのリンクがないことを考えると、おそらくこのためのパラメーター/クラス属性を作成するのが最善です-一般的に望ましい動作であるとは思いません。Hth
Danny W. Adair

モデルから設定されているreadonly_fieldsに関しては、フォームをオーバーライドして他のフィールドを追加した場合、これはおそらく機能しません...実際のフォームフィールドに基づいた方がおそらく良いでしょう。
ダニーW.アデア

これは機能しませんでした:def __init __(self、* args):super(RegistrationStatusAdmin、self).__ init __(* args)self.display_links = []
Steve Bennett

50

モデルを作成するために使用しているクラスと、インラインで読み取り専用のクラスの2つを次に示します。

モデル管理者の場合:

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

両方のクラスを1つのサブクラスに適用する方法。たとえば、クラスに通常のフィールドとインラインがある場合はどうなりますか?両方を拡張できますか?
ティモ

@timoはこれらのクラスをミックスインとして
MartinM 2017

1
has_add_permissioninReadOnlyAdminはリクエストのみをパラメータとして受け取ります
MartinM 2017

has_change_permission()もオーバーライドする必要があります。def has_change_permission(self、request、obj = None):
david euler

13

ユーザーに編集できないことを認識させたい場合は、最初の解決策で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

2番目:読み取り専用ソリューションはプレーンモデルで正常に機能します。ただし、外部キーを持つ継承モデルがある場合は機能しません。残念ながら、その解決策はまだわかりません。良い試みは次のとおりです。

読み取り専用としてのモデル全体

しかし、それは私にとってもうまくいきません。

最後に、幅広いソリューションについて考えたい場合は、各インラインも読み取り専用にする必要があることを強制する必要があります。


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
これにより、フィールドを表示するためにインスタンスを開くことは禁止されていますが、リストするだけで問題がなければ、機能します。
セバスティアンVansteenkiste

8

これは、2018年8月1日にリリースされたDjango2.1に追加されました。

ModelAdmin.has_view_permission()既存のhas_delete_permission、has_change_permission、has_add_permissionとまったく同じです。こちらのドキュメントそれについて読むことができます

リリースノートから:

これにより、ユーザーは管理者のモデルに読み取り専用でアクセスできます。ModelAdmin.has_view_permission()は新しいものです。実装には下位互換性があり、「変更」権限を持つユーザーがオブジェクトを編集できるようにするために「表示」権限を割り当てる必要はありません。


ただし、スーパーユーザーは管理インターフェースでオブジェクトを変更できますよね?
Flimm

これらのメソッドの1つをオーバーライドして動作を変更し、スーパーユーザーのアクセスを禁止しない限り、これは正しいです。
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

私はこれをDjango1.11 / Python3でのみ試しました。


Djangoを使ってから久しぶりです。他の誰かがこれを保証できますか?
スティーブベネット

@SteveBennettㄹこれが対処する要件には多くのバリエーションがあります...この答えは水密ではありません...ここで説明を提案してください:stackoverflow.com/a/36019597/2586761とあなたがstackoverflow.comにコメントした答え/ a / 33543817/2586761受け入れられた回答よりも完全
ptim 2017

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_fieldsそしてactions線は必要ない
cheng10

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 adminの特定のユーザーに対してすべてのフィールドを読み取り専用にする必要があるときに同じ要件に遭遇し、自分のコードをロールせずに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. Pythonmanage.py移行
  4. python manage.py runserver 6666

ok。「ビュー」権限を楽しんでください


0

インラインを含むユーザー権限に応じて読み取り専用ビューを処理する汎用クラスを作成しました;)

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.