Django: Как добавить пользовательскую кнопку на страницу изменения формы администратора, которая выполняет действие администратора?
Я уже определил пользовательское действие администратора для моей модели, которое работает отлично, как и ожидалось. Я также рассмотрел несколько способов добавления кнопки для изменения формы страницы администратора здесь, на SO. Единственный пропущенный мной шаг - как заставить кнопку на странице формы изменений выполнить моё пользовательское действие администратора с текущим объектом.
Цель состоит в том, чтобы позволить администратору осматривать каждый объект индивидуально и выполнять над ним действие, не возвращаясь к представлению списка, не выбирая проверенный объект и не выполняя действие из списка.
Мое пользовательское действие администратора выглядит так:
def admin_apply_change(modeladmin, request, queryset):
# loop over objects in query set and perform action
Я предполагаю, что существует простой и понятный способ вызова этого действия в форме изменения администратора, где queryset
будет содержать только открытый в данный момент объект, на который смотрит администратор.
ПРИМЕЧАНИЕ. Желательно, чтобы кнопка находилась внизу формы изменений, рядом с Save
кнопка вместо того, чтобы быть на вершине с History
что не очень видно.
Решение
Смотрите ответ Remi Smirra для решения. Для того, чтобы это работало, необходимы следующие исправления:
1: в переопределении response_change
отсутствует инициализация некоторых переменных:
opts = self.model._meta
pk_value = obj._get_pk_val()
preserved_filters = self.get_preserved_filters(request)
2: Новый тег включения custom_submit_row
должны быть помещены в теги-шаблоны, а не в администратор (см. документы для пользовательских тегов-шаблонов)
3: Это упущение, на которое вы можете потерять некоторое время. В change_form.html
вам нужно не только изменить предложенную строку:
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
но и более важная линия внизу, где submit_row
появляется:
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
(он расположен чуть выше блока javascript в change_form.html
)
5 ответов
Вы можете взглянуть на change_form_template и установить его на свой собственный шаблон и переопределить response_change
метод:
class MyModelAdmin(admin.ModelAdmin):
# A template for a customized change view:
change_form_template = 'path/to/your/custom_change_form.html'
def response_change(self, request, obj):
opts = self.model._meta
pk_value = obj._get_pk_val()
preserved_filters = self.get_preserved_filters(request)
if "_customaction" in request.POST:
# handle the action on your obj
redirect_url = reverse('admin:%s_%s_change' %
(opts.app_label, opts.model_name),
args=(pk_value,),
current_app=self.admin_site.name)
redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url)
return HttpResponseRedirect(redirect_url)
else:
return super(MyModelAdmin, self).response_change(request, obj)
Скопируйте change_form.html
от твоего site-packages/django/contrib/admin/templates/change_form.html
и отредактируйте строку 44
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
в
{% if save_on_top %}{% block submit_buttons_top %}{% custom_submit_row %}{% endblock %}{% endif %}
Также проверьте строку:
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
чуть выше блока javascript.
Затем вы можете зарегистрировать новый тег включения где-нибудь в вашем admin.py или добавить его в templatetags:
@register.inclusion_tag('path/to/your/custom_submit_line.html', takes_context=True)
def custom_submit_row(context):
"""
Displays the row of buttons for delete and save.
"""
opts = context['opts']
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
ctx = {
'opts': opts,
'show_delete_link': (
not is_popup and context['has_delete_permission'] and
change and context.get('show_delete', True)
),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': (
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
'show_save_and_continue': not is_popup and context['has_change_permission'],
'is_popup': is_popup,
'show_save': True,
'preserved_filters': context.get('preserved_filters'),
}
if context.get('original') is not None:
ctx['original'] = context['original']
return ctx
Содержание вашего custom_submit_line.html
:
{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% trans "Delete" %}</a></p>
{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
<input type="submit" value="{% trans 'Custom Action' %}" name="_customaction" />
</div>
Это много кода, но в основном копировать / вставить. Надеюсь, это поможет.
Большинство людей, вероятно, делают это, не задумываясь, хотя из ответа не было ясно, что форму смены администратора следует просто расширять, а не перезаписывать полностью.
custom_change_form.html
{% extends "admin/change_form.html" %}
{% if save_on_top %}{% block submit_buttons_top %}{% custom_submit_row %}{% endblock %}{% endif %}
{% block submit_buttons_bottom %}{% custom_submit_row %}{% endblock %}
Кроме того, вы можете просто расширить файл submit_line.html, добавив свою пользовательскую кнопку (как вверху, так и внизу страницы изменений).
Ваши шаблоны файлов /adminyour_app_name/your_model_name.html будут начинаться с:
{% extends "admin/submit_line.html" %}
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
... YOUR BUTTONS HERE ...
{% endblock %}
</div>
На страницу в администраторе Django вы можете добавить специальную кнопку, которая запускает действие администратора.
Например, сначала скопируйте изdjango/contrib/admin/templates/admin/submit_line.html
в вашей виртуальной среде на или переопределить его, как показано ниже. * вtemplates/admin/
,templates/admin/app1/
илиtemplates/admin/app1/person/
применяется ко всем администраторам во всех приложениях, ко всем администраторам только или толькоperson
только администраторapp1
соответственно, и вы можете увидеть исходный submit_line.html:
Django Project
|-core
| └-settings.py
|-app1
| |-models.py
| └-admin.py
|-app2
└-templates
└-admin
|-app1
| |-person
| | └-submit_line.html # Or
| |-model1
| |-model2
| └-submit_line.html # Or
|-app2
└-submit_line.html # Or
Затем добавьте{% if custom_button %}<input ...
кsubmit_line.html
как показано ниже:
# "templates/admin/submit_line.html" Or
# "templates/admin/app1/submit_line.html" Or
# "templates/admin/app1/person/submit_line.html"
# ...
{% if show_delete_link and original %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% translate "Delete" %}</a></p>
{% endif %}
{# ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ #}
{% if custom_button %}<input type="submit" value="{% translate 'Custom button' %}" name="_custom_button">{% endif %}
{# ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ #}
{% if show_save_as_new %}<input type="submit" value="{% translate 'Save as new' %}" name="_saveasnew">{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% translate 'Save and add another' %}" name="_addanother">{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% translate 'Save and add another' %}" name="_addanother">{% endif %}
# ...
И, установитеBASE_DIR / 'templates'
к'DIRS'
вTEMPLATES
вsettings.py
как показано ниже:
# "core/settings.py"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / 'templates' # Here
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
И есть модель вmodels.py
как показано ниже:
# "app1/models.py"
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
И есть администратор с действием администратора вadmin.py
как показано ниже:
# "app1/admin.py"
from django.contrib import admin, messages
from .models import Person
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
actions = ("uppercase", )
# Here
@admin.action(description='Make selected persons uppercase')
def uppercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.upper()
obj.save()
messages.success(request, "Successfully made uppercase!")
Итак, если вы используетеuppercase
действие администратора, как показано ниже:
Затем вы можете перевести выбранных людей в верхний регистр, как показано ниже:
Пользовательская кнопка еще не отображается на странице, как показано ниже:
Теперь переопределите Change_view() и response_change() в администраторе, как показано ниже. *Вы можете увидеть исходные функции Change_view() и response_change():
# "app1/admin.py"
from django.contrib import admin, messages
from .models import Person
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
actions = ("uppercase", )
@admin.action(description='Make selected persons uppercase')
def uppercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.upper()
obj.save()
messages.success(request, "Successfully made uppercase!")
# Here
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['custom_button'] = True
return self.changeform_view(request, object_id, form_url, extra_context)
# Here
def response_change(self, request, obj):
if "_custom_button" in request.POST:
queryset = self.get_queryset(request).filter(id=obj.id)
self.uppercase(request, queryset)
return super().response_change(request, obj)
Затем на экране появится пользовательская кнопка.change
странице, как показано ниже, затем, если вы нажмете на пользовательскую кнопку:
Затем вы можете перевести имя человека в верхний регистр, как показано ниже:
Кроме того, переопределите add_view() и response_add() вPerson
администратор, как показано ниже. *Вы можете увидеть оригинальные add_view() и response_add():
# "app1/admin.py"
from django.contrib import admin, messages
from .models import Person
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
actions = ("uppercase", )
@admin.action(description='Make selected persons uppercase')
def uppercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.upper()
obj.save()
messages.success(request, "Successfully made uppercase!")
# Here
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context['custom_button'] = True
return self.changeform_view(request, None, form_url, extra_context)
# Here
def response_add(self, request, obj, post_url_continue=None):
if "_custom_button" in request.POST:
queryset = self.get_queryset(request).filter(id=obj.id)
self.uppercase(request, queryset)
return super().response_add(request, obj, post_url_continue)
Затем на экране появится пользовательская кнопка.add
странице, как показано ниже, затем, если вы нажмете на пользовательскую кнопку:
Затем вы можете перевести имя человека в верхний регистр, как показано ниже:
На основе ответа Реми более чистое решение для отмены шаблонов
submit-row
в шаблоне.
{% extends "admin/submit_line.html" %}
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
{{ block.super }}
{% if custom_buttons_template %}{% include custom_buttons_template %}{% endif %}
{% endblock %}
В
change_view
вы можете добавить в контекст, чтобы настраивать пользовательские кнопки для каждой модели отдельно.
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['custom_buttons_template'] = 'admin/test.html'
return super(TransactionFileAdmin, self).change_view(
request, object_id, form_url, extra_context=extra_context,
)
После этого мой шаблон загрузится
admin/test.html
и вставьте контент прямо в строку отправки перед
Save
кнопка. Если вы хотите разместить свои кнопки в другом месте, вы можете скопировать полностью
submit_line.html
и положи
custom_buttons_template
в любом месте.