管理者のアクション
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
関数にshort_description
属性を与えることで、よりわかりやすく、より人間にわかりやすい名前を付けることができます。
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
ノート
これは見覚えがあるかもしれません。 管理者のlist_display
オプションも同じ手法を使用して、そこに登録されているコールバック関数の人間が読める形式の説明を提供します。
ModelAdmin へのアクションの追加
次に、 ModelAdmin にアクションを通知する必要があります。 これは、他の構成オプションと同じように機能します。 したがって、アクションとその登録を含む完全なadmin.py
は、次のようになります。
from django.contrib import admin
from myapp.models import Article
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
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']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"
最初に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 のすべてのアクションを無効にする
特定の ModelAdmin で no バルクアクションを使用できるようにする場合は、 ModelAdmin.actions をNone
に設定します。
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
アクションの権限を設定する
アクションは、アクション関数にallowed_permissions
属性を設定することにより、特定の権限を持つユーザーに可用性を制限する場合があります。
def make_published(modeladmin, request, queryset):
queryset.update(status='p')
make_published.allowed_permissions = ('change',)
make_published()
アクションは、 ModelAdmin.has_change_permission()チェックに合格したユーザーのみが使用できます。
allowed_permissions
に複数の権限がある場合、ユーザーが少なくとも1つのチェックに合格している限り、アクションを使用できます。
allowed_permissions
および対応するメソッドチェックに使用できる値は次のとおりです。
'add'
: ModelAdmin.has_add_permission()'change'
: ModelAdmin.has_change_permission()'delete'
: ModelAdmin.has_delete_permission()'view'
: ModelAdmin.has_view_permission()
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']
def make_published(self, request, queryset):
queryset.update(status='p')
make_published.allowed_permissions = ('publish',)
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))