Django adminに対して、実行するオブジェクトを選択する必要のないカスタム管理アクションを作成することは可能ですか?
オブジェクトを選択せずにアクションを実行しようとすると、次のメッセージが表示されます。
Items must be selected in order to perform actions on them. No items have been changed.
この動作をオーバーライドして、とにかくアクションを実行させる方法はありますか?
ユウジは正しい方向に進んでいますが、私はあなたのために働くかもしれないより簡単な解決策を使用しました。以下のようにresponse_actionをオーバーライドすると、チェックが行われる前に、空のクエリセットをすべてのオブジェクトを含むクエリセットに置き換えることができます。このコードは、実行しているアクションをチェックして、クエリセットを変更する前にすべてのオブジェクトでの実行が承認されていることを確認するため、特定の場合にのみ発生するように制限できます。
def response_action(self, request, queryset):
# override to allow for exporting of ALL records to CSV if no chkbox selected
selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
if request.META['QUERY_STRING']:
qd = dictify_querystring(request.META['QUERY_STRING'])
else:
qd = None
data = request.POST.copy()
if len(selected) == 0 and data['action'] in ('export_to_csv', 'extended_export_to_csv'):
ct = ContentType.objects.get_for_model(queryset.model)
klass = ct.model_class()
if qd:
queryset = klass.objects.filter(**qd)[:65535] # cap at classic Excel maximum minus 1 row for headers
else:
queryset = klass.objects.all()[:65535] # cap at classic Excel maximum minus 1 row for headers
return getattr(self, data['action'])(request, queryset)
else:
return super(ModelAdminCSV, self).response_action(request, queryset)
受け入れられた答えはDjango 1.6では機能しなかったので、私はこれで終わりました:
from Django.contrib import admin
class AdvWebUserAdmin(admin.ModelAdmin):
....
def changelist_view(self, request, extra_context=None):
if 'action' in request.POST and request.POST['action'] == 'your_action_here':
if not request.POST.getlist(admin.ACTION_CHECKBOX_NAME):
post = request.POST.copy()
for u in MyModel.objects.all():
post.update({admin.ACTION_CHECKBOX_NAME: str(u.id)})
request._set_post(post)
return super(AdvWebUserAdmin, self).changelist_view(request, extra_context)
いつ my_action
が呼び出され、何も選択されていない場合は、db内のすべてのMyModel
インスタンスを選択します。
私はこれが欲しかったのですが、最終的には使用しないことにしました。今後の参考のためにここに投稿してください。
アクションにプロパティ(acts_on_all
など)を追加します。
def my_action(modeladmin, request, queryset):
pass
my_action.short_description = "Act on all %(verbose_name_plural)s"
my_action.acts_on_all = True
ModelAdmin
で、changelist_view
をオーバーライドして、プロパティを確認します。
要求メソッドがPOSTであり、アクションが指定されていて、呼び出し可能なアクションのプロパティがTrueに設定されている場合は、選択したオブジェクトを表すリストを変更します。
def changelist_view(self, request, extra_context=None):
try:
action = self.get_actions(request)[request.POST['action']][0]
action_acts_on_all = action.acts_on_all
except (KeyError, AttributeError):
action_acts_on_all = False
if action_acts_on_all:
post = request.POST.copy()
post.setlist(admin.helpers.ACTION_CHECKBOX_NAME,
self.model.objects.values_list('id', flat=True))
request.POST = post
return admin.ModelAdmin.changelist_view(self, request, extra_context)
この動作をオーバーライドして、とにかくアクションを実行させる方法はありますか?
簡単な方法はありません。
エラーメッセージをgrepすると、コードがDjango.contrib.admin.options.py
にあり、問題のコードがchangelist_viewの奥深くにあることがわかります。
action_failed = False
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
# Actions with no confirmation
if (actions and request.method == 'POST' and
'index' in request.POST and '_save' not in request.POST):
if selected:
response = self.response_action(request, queryset=cl.get_query_set())
if response:
return response
else:
action_failed = True
else:
msg = _("Items must be selected in order to perform "
"actions on them. No items have been changed.")
self.message_user(request, msg)
action_failed = True
これはresponse_action
関数でも使用されるため、changelist_templateをオーバーライドしてそれを使用することはできません。独自のアクション有効性チェッカーとランナーを定義するのが最も簡単です。
そのドロップダウンリストを本当に使用したい場合は、これが保証のないアイデアです。
選択のない管理アクションの新しい属性を定義するのはどうですか:myaction.selectionless = True
オーバーライドされたresponse_action
のchangelist_view
機能をある程度コピーします。これは、特定のフラグが指定されたアクションでのみ機能し、「実際の」changelist_view
を返します。
# There can be multiple action forms on the page (at the top
# and bottom of the change list, for example). Get the action
# whose button was pushed.
try:
action_index = int(request.POST.get('index', 0))
except ValueError:
action_index = 0
# Construct the action form.
data = request.POST.copy()
data.pop(helpers.ACTION_CHECKBOX_NAME, None)
data.pop("index", None)
# Use the action whose button was pushed
try:
data.update({'action': data.getlist('action')[action_index]})
except IndexError:
# If we didn't get an action from the chosen form that's invalid
# POST data, so by deleting action it'll fail the validation check
# below. So no need to do anything here
pass
action_form = self.action_form(data, auto_id=None)
action_form.fields['action'].choices = self.get_action_choices(request)
# If the form's valid we can handle the action.
if action_form.is_valid():
action = action_form.cleaned_data['action']
select_across = action_form.cleaned_data['select_across']
func, name, description = self.get_actions(request)[action]
if func.selectionless:
func(self, request, {})
'実際の'アクションが呼び出された場合でもエラーが発生します。オーバーライドされたアクションが呼び出された場合、request.POSTを変更してアクションを削除できる可能性があります。
他の方法では、ハッキングが多すぎます。少なくとも私は思います。
さて、これを機能させたいと思うほど頑固な人にとっては、これは醜いハック(Django 1.3))であり、何も選択しなくてもすべてのアクションを実行できます。
元のchangelist_viewをだまして、何かが選択されていると思い込ませる必要があります。
class UsersAdmin(admin.ModelAdmin):
def changelist_view(self, request, extra_context=None):
post = request.POST.copy()
if helpers.ACTION_CHECKBOX_NAME not in post:
post.update({helpers.ACTION_CHECKBOX_NAME:None})
request._set_post(post)
return super(ContributionAdmin, self).changelist_view(request, extra_context)
そのため、modeladminで、request.POSTに追加するchangelist_viewをオーバーライドします。このキーは、選択したオブジェクトのIDを保存するためにDjangoが使用します。
アクションでは、次の項目が選択されていないかどうかを確認できます。
if queryset == None:
do_your_stuff()
言うまでもなく、これを行うことは想定されていません。
オブジェクトの選択は必要なものの一部ではないため、独自の管理ビューを作成することをお勧めします。
独自の管理ビューを作成するのは非常に簡単です。
@staff_member_required
デコレータこれに関連する新しい1.1機能 を使用することもできますが、今説明したように実行する方が簡単な場合があります。
次のミックスインを使用して、ユーザーが少なくとも1つのオブジェクトを選択する必要のないアクションを作成します。また、ユーザーがフィルタリングしたクエリセットを取得することもできます: https://Gist.github.com/rafen/eff7adae38903eee76600cff40b8b659
ここにそれを使用する方法の例があります(リンクでそれを使用する方法の詳細があります):
@admin.register(Contact)
class ContactAdmin(ExtendedActionsMixin, admin.ModelAdmin):
list_display = ('name', 'country', 'state')
actions = ('export',)
extended_actions = ('export',)
def export(self, request, queryset):
if not queryset:
# if not queryset use the queryset filtered by the URL parameters
queryset = self.get_filtered_queryset(request)
# As usual do something with the queryset
行ごとに1回アクションを呼び出さないように、@ AndyTheEntity応答に変更を加えました。
def changelist_view(self, request, extra_context=None):
actions = self.get_actions(request)
if (actions and request.method == 'POST' and 'index' in request.POST and
request.POST['action'].startswith('generate_report')):
data = request.POST.copy()
data['select_across'] = '1'
request.POST = data
response = self.response_action(request, queryset=self.get_queryset(request))
if response:
return response
return super(BaseReportAdmin, self).changelist_view(request, extra_context)
私が見つけた最も簡単な解決策は、Django admin関数をDjango docs に従って作成し、Webサイトの管理者が任意のオブジェクトをランダムに選択することです。関数を実行します。これにより、アイテムが関数に渡されますが、どこでも使用しないため、冗長になります。