管理アクション—Djangoドキュメント

提供:Dev Guides
< DjangoDjango/docs/3.2.x/ref/contrib/admin/actions
移動先:案内検索

管理者のアクション

Djangoの管理者の基本的なワークフローは、一言で言えば、「オブジェクトを選択してから変更する」ことです。 これは、ほとんどのユースケースでうまく機能します。 ただし、一度に多くのオブジェクトに同じ変更を加える必要がある場合、このワークフローは非常に面倒な場合があります。

このような場合、Djangoの管理者は、「アクション」(変更リストページで選択されたオブジェクトのリストで呼び出される関数)を記述して登録することができます。

管理者の変更リストを見ると、この機能が動作していることがわかります。 Djangoには、すべてのモデルで利用できる「選択したオブジェクトの削除」アクションが付属しています。 たとえば、Djangoの組み込み django.contrib.auth アプリのユーザーモジュールは次のとおりです。

[[../File:../../../_images/admin-actions|../../../_images/admin-actions.png]]

警告

「選択したオブジェクトの削除」アクションは、効率上の理由から QuerySet.delete()を使用しますが、これには重要な注意事項があります。モデルのdelete()メソッドは呼び出されません。

この動作をオーバーライドする場合は、 ModelAdmin.delete_queryset()をオーバーライドするか、任意の方法で削除を行うカスタムアクションを作成できます。たとえば、それぞれに対してModel.delete()を呼び出します。選択したアイテム。

一括削除の背景については、オブジェクトの削除に関するドキュメントを参照してください。


このリストに独自のアクションを追加する方法については、以下をお読みください。

アクションを書く

アクションを説明する最も簡単な方法は例によるものなので、詳しく見ていきましょう。

管理アクションの一般的な使用例は、モデルの一括更新です。 Articleモデルのニュースアプリケーションを想像してみてください。

from django.db import models

STATUS_CHOICES = [
    ('d', 'Draft'),
    ('p', 'Published'),
    ('w', 'Withdrawn'),
]

class Article(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    status = models.CharField(max_length=1, choices=STATUS_CHOICES)

    def __str__(self):
        return self.title

このようなモデルで実行する可能性のある一般的なタスクは、記事のステータスを「ドラフト」から「公開済み」に更新することです。 管理者で一度に1つの記事でこれを簡単に行うことができますが、記事のグループを一括公開する場合は面倒です。 それでは、記事のステータスを「公開済み」に変更できるアクションを作成しましょう。

アクション関数の記述

まず、管理者からアクションがトリガーされたときに呼び出される関数を作成する必要があります。 アクション関数は、次の3つの引数を取る通常の関数です。

  • 現在の ModelAdmin
  • 現在のリクエストを表す HttpRequest
  • ユーザーが選択したオブジェクトのセットを含む QuerySet

これらの記事の公開関数は ModelAdmin またはリクエストオブジェクトを必要としませんが、クエリセットを使用します。

def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

ノート

最高のパフォーマンスを得るには、クエリセットの更新メソッドを使用しています。 他のタイプのアクションでは、各オブジェクトを個別に処理する必要がある場合があります。 このような場合、クエリセットを繰り返し処理します。

for obj in queryset:
    do_something_with(obj)

実際には、アクションを作成するために必要なのはこれだけです。 ただし、オプションですが便利な手順をもう1つ実行し、管理者に「素敵な」タイトルを付けます。 デフォルトでは、このアクションは「公開する」としてアクションリストに表示されます。これは関数名であり、アンダースコアはスペースに置き換えられています。 それは問題ありませんが、make_published関数の action()デコレータを使用することで、より適切で人間にわかりやすい名前を付けることができます。

from django.contrib import admin

...

@admin.action(description='Mark selected stories as published')
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

ノート

これは見覚えがあるかもしれません。 管理者の list_display オプションは、 display()デコレータと同様の手法を使用して、そこに登録されているコールバック関数の人間が読める形式の説明も提供します。


バージョン3.2で変更: action()デコレータのdescription引数は、前のアクション関数にshort_description属性を直接設定するのと同じです。バージョン。 下位互換性のために、属性を直接設定することは引き続きサポートされています。


ModelAdmin へのアクションの追加

次に、 ModelAdmin にアクションを通知する必要があります。 これは、他の構成オプションと同じように機能します。 したがって、アクションとその登録を含む完全なadmin.pyは、次のようになります。

from django.contrib import admin
from myapp.models import Article

@admin.action(description='Mark selected stories as published')
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'status']
    ordering = ['title']
    actions = [make_published]

admin.site.register(Article, ArticleAdmin)

そのコードは、次のような管理者変更リストを提供します。

[[../File:../../../_images/adding-actions-to-the-modeladmin|../../../_images/adding-actions-to-the-modeladmin.png]] それが本当にすべてです! 独自のアクションを作成したい場合は、開始するのに十分な知識があります。 このドキュメントの残りの部分では、より高度な手法について説明します。


アクションのエラーの処理

アクションの実行中に発生する可能性のある予測可能なエラー状態がある場合は、問題をユーザーに適切に通知する必要があります。 これは、例外を処理し、 django.contrib.admin.ModelAdmin.message_user()を使用して、応答に問題のユーザーフレンドリーな説明を表示することを意味します。


高度なアクションテクニック

より高度なオプションに利用できる追加のオプションと可能性がいくつかあります。

ModelAdmin メソッドとしてのアクション

上記の例は、関数として定義されたmake_publishedアクションを示しています。 これはまったく問題ありませんが、コード設計の観点からは完全ではありません。アクションはArticleオブジェクトに緊密に結合されているため、アクションをArticleAdminオブジェクト自体にフックすることは理にかなっています。

あなたはこのようにそれを行うことができます:

class ArticleAdmin(admin.ModelAdmin):
    ...

    actions = ['make_published']

    @admin.action(description='Mark selected stories as published')
    def make_published(self, request, queryset):
        queryset.update(status='p')

最初にmake_publishedをメソッドに移動し、modeladminパラメーターの名前をselfに変更し、次に文字列'make_published'を配置したことに注意してください。直接関数参照の代わりにactionsで。 これは、 ModelAdmin にアクションをメソッドとして検索するように指示します。

アクションをメソッドとして定義すると、アクションは ModelAdmin 自体に慣用的にアクセスできるようになり、管理者が提供する任意のメソッドをアクションで呼び出すことができます。

たとえば、selfを使用して、アクションが成功したことを通知するメッセージをユーザーにフラッシュできます。

from django.contrib import messages
from django.utils.translation import ngettext

class ArticleAdmin(admin.ModelAdmin):
    ...

    def make_published(self, request, queryset):
        updated = queryset.update(status='p')
        self.message_user(request, ngettext(
            '%d story was successfully marked as published.',
            '%d stories were successfully marked as published.',
            updated,
        ) % updated, messages.SUCCESS)

これにより、アクションが正常に実行された後、管理者自身が実行するアクションと一致します。

[[../File:../../../_images/actions-as-modeladmin-methods|../../../_images/actions-as-modeladmin-methods.png]]

中間ページを提供するアクション

デフォルトでは、アクションが実行された後、ユーザーは元の変更リストページにリダイレクトされます。 ただし、一部のアクション、特により複雑なアクションでは、中間ページを返す必要があります。 たとえば、組み込みの削除アクションは、選択したオブジェクトを削除する前に確認を求めます。

中間ページを提供するには、アクションから HttpResponse (またはサブクラス)を返します。 たとえば、Djangoのシリアル化関数を使用して、選択したオブジェクトをJSONとしてダンプするエクスポート関数を作成できます。

from django.core import serializers
from django.http import HttpResponse

def export_as_json(modeladmin, request, queryset):
    response = HttpResponse(content_type="application/json")
    serializers.serialize("json", queryset, stream=response)
    return response

一般的に、上記のようなものは素晴らしいアイデアとは見なされません。 ほとんどの場合、ベストプラクティスは、 HttpResponseRedirect を返し、ユーザーを作成したビューにリダイレクトして、選択したオブジェクトのリストをGETクエリ文字列に渡すことです。 これにより、中間ページに複雑なインタラクションロジックを提供できます。 たとえば、より完全なエクスポート機能を提供したい場合は、ユーザーがフォーマットを選択できるようにし、場合によってはエクスポートに含めるフィールドのリストを選択できるようにします。 最善の方法は、カスタムエクスポートビューにリダイレクトする小さなアクションを作成することです。

from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect

def export_selected_objects(modeladmin, request, queryset):
    selected = queryset.values_list('pk', flat=True)
    ct = ContentType.objects.get_for_model(queryset.model)
    return HttpResponseRedirect('/export/?ct=%s&ids=%s' % (
        ct.pk,
        ','.join(str(pk) for pk in selected),
    ))

ご覧のとおり、アクションはかなり短いです。 すべての複雑なロジックは、エクスポートビューに属します。 これは、あらゆるタイプのオブジェクトを処理する必要があるため、ContentTypeを使用します。

このビューを書くことは、読者の練習問題として残されています。


アクションをサイト全体で利用できるようにする

AdminSite.add_action(action, name=None)

一部のアクションは、管理サイトの任意のオブジェクトで使用できるようになっている場合に最適です。上記で定義したエクスポートアクションが適しています。 AdminSite.add_action()を使用して、アクションをグローバルに使用可能にすることができます。 例えば:

from django.contrib import admin

admin.site.add_action(export_selected_objects)

これにより、export_selected_objectsアクションが「export_selected_objects」という名前のアクションとしてグローバルに使用できるようになります。 AdminSite.add_action()に2番目の引数を渡すことで、アクションに明示的に名前を付けることができます(後でプログラムでアクションを削除する場合に適しています)。

admin.site.add_action(export_selected_objects, 'export_selected')


アクションを無効にする

特定のオブジェクトに対して、特定のアクション、特にサイト全体で登録されたを無効にする必要がある場合があります。 アクションを無効にできる方法はいくつかあります。

サイト全体のアクションを無効にする

AdminSite.disable_action(name)

サイト全体のアクションを無効にする必要がある場合は、 AdminSite.disable_action()を呼び出すことができます。

たとえば、このメソッドを使用して、組み込みの「選択したオブジェクトの削除」アクションを削除できます。

admin.site.disable_action('delete_selected')

上記を実行すると、そのアクションはサイト全体で利用できなくなります。

ただし、特定のモデルに対してグローバルに無効化されたアクションを再度有効にする必要がある場合は、ModelAdmin.actionsリストに明示的にリストしてください。

# Globally disable delete selected
admin.site.disable_action('delete_selected')

# This ModelAdmin will not have delete_selected available
class SomeModelAdmin(admin.ModelAdmin):
    actions = ['some_other_action']
    ...

# This one will
class AnotherModelAdmin(admin.ModelAdmin):
    actions = ['delete_selected', 'a_third_action']
    ...


特定の ModelAdmin のすべてのアクションを無効にする

特定の ModelAdminno バルクアクションを使用できるようにする場合は、 ModelAdmin.actionsNoneに設定します。

class MyModelAdmin(admin.ModelAdmin):
    actions = None

これは、 ModelAdmin に、サイト全体のアクションを含むアクションを表示または許可しないように指示します。


アクションを条件付きで有効または無効にする

ModelAdmin.get_actions(request)

最後に、 ModelAdmin.get_actions()をオーバーライドすることで、リクエストごとに(したがって、ユーザーごとに)アクションを条件付きで有効または無効にできます。

これにより、許可されたアクションのディクショナリが返されます。 キーはアクション名であり、値は(function, name, short_description)タプルです。

たとえば、名前が「J」で始まるユーザーのみがオブジェクトを一括で削除できるようにする場合は、次のようにします。

class MyModelAdmin(admin.ModelAdmin):
    ...

    def get_actions(self, request):
        actions = super().get_actions(request)
        if request.user.username[0].upper() != 'J':
            if 'delete_selected' in actions:
                del actions['delete_selected']
        return actions


アクションの権限を設定する

アクションは、アクション関数を action()デコレータでラップし、permissions引数を渡すことにより、特定の権限を持つユーザーに可用性を制限する場合があります。

@admin.action(permissions=['change'])
def make_published(modeladmin, request, queryset):
    queryset.update(status='p')

make_published()アクションは、 ModelAdmin.has_change_permission()チェックに合格したユーザーのみが使用できます。

permissionsに複数の権限がある場合、ユーザーが少なくとも1つのチェックに合格している限り、アクションを使用できます。

permissionsおよび対応するメソッドチェックに使用できる値は次のとおりです。

ModelAdminに対応するhas_<value>_permission(self, request)メソッドを実装する限り、他の値を指定できます。

例えば:

from django.contrib import admin
from django.contrib.auth import get_permission_codename

class ArticleAdmin(admin.ModelAdmin):
    actions = ['make_published']

    @admin.action(permissions=['publish'])
    def make_published(self, request, queryset):
        queryset.update(status='p')

    def has_publish_permission(self, request):
        """Does the user have the publish permission?"""
        opts = self.opts
        codename = get_permission_codename('publish', opts)
        return request.user.has_perm('%s.%s' % (opts.app_label, codename))

バージョン3.2で変更: action()デコレータのpermissions引数は、前のアクション関数にallowed_permissions属性を直接設定するのと同じです。バージョン。 下位互換性のために、属性を直接設定することは引き続きサポートされています。


actionデコレータ

action(*, permissions=None, description=None)

バージョン3.2の新機能。

このデコレータは、アクションで使用できるカスタムアクション関数に特定の属性を設定するために使用できます。

@admin.action(
    permissions=['publish'],
    description='Mark selected stories as published',
)
def make_published(self, request, queryset):
    queryset.update(status='p')

これは、関数にいくつかの属性(元の長い名前を使用)を直接設定することと同じです。

def make_published(self, request, queryset):
    queryset.update(status='p')
make_published.allowed_permissions = ['publish']
make_published.short_description = 'Mark selected stories as published'

このデコレータの使用は、アクション関数を作成するために必須ではありませんが、関数の目的を識別するためのソースのマーカーとして引数なしで使用すると便利な場合があります。

@admin.action
def make_inactive(self, request, queryset):
    queryset.update(is_active=False)

この場合、関数に属性は追加されません。