Возможно ли добавить поле ввода в пользовательские массовые действия Wagtails?
Возможно ли добавить поле ввода в Wagtails custom bulk actions?
В шаблоне из примера документации есть блок под названием form_section
. Здесь я хочу добавить отдельную форму для добавления еще одного поля ввода. Разумеется, возможна и другая позиция.
<!-- /path/to/confirm_bulk_import.html -->
# ...
{% block form_section %}
{% if images %}
{% trans 'Yes, import' as action_button_text %}
{% trans "No, don't import" as no_action_button_text %}
# Can I use my own confirmation form here? How about its view?:
{% include 'wagtailadmin/bulk_actions/confirmation/form.html' with action_button_class="serious" %}
{% else %}
{% include 'wagtailadmin/bulk_actions/confirmation/go_back.html' %}
{% endif %}
{% endblock form_section %}
Я хотел бы массово выбрать Image
экземпляры, чтобы добавить их в Page
. Поэтому мне нужно иметь ChoiceField
для выбора Page
. Для этого также потребуется настраиваемая View
для логики, лежащей в основе этого "импорта". Последнее не является вопросом. Мне просто интересно, как я могу добавить это поле ввода и изменить вид этих чудесных массовых действий.
Wagtail использует чистый HTML и CSS. Поэтому все, что приходит на сторону python
, принимается через HTML форму. Это означает, что каждое нажатие кнопки в UI должно ассоциироваться с HTML form
и со стороны wagtail вы можете найти его в request
.
Метод выполнения действия
Если вы просмотрели документацию bulk action documentation, то обнаружили, что после отправки form
будет выполнен метод класса execute_action
. Теперь вам нужно понять параметры этого метода.
@classmethod
def execute_action(cls, objects, **kwargs):
raise NotImplementedError("execute_action needs to be implemented")
Поскольку это метод класса, первым параметром является тип класса, на котором работает этот метод. Вы можете узнать больше о методах класса в документации python.
Второй параметр objects
- это список объектов, которые вы выбрали для этой массовой операции. Если быть точным, это список объектов, которые вы выбрали с правильным уровнем разрешения. В реализации по умолчанию разрешение дается для всех объектов. Но вы можете переопределить это поведение.
def check_perm(self, obj):
return True
Вы можете переопределить этот метод в своем пользовательском классе массового действия и проверять разрешение для каждого объекта. В качестве параметра objects
вы получите только те объекты, которые имеют check_perm(obj)==True
, из списка выбранных вами объектов.
Третьим параметром метода класса execute_action
является список аргументов с ключевым словом (точнее, словарь). Этот словарь получается при вызове следующего метода.
def get_execution_context(self):
return {}
По умолчанию этот метод возвращает пустой словарь. Но вы можете переопределить его для отправки чего угодно. Поскольку execute_action
является методом класса, он не может получить доступ к переменным экземпляра. Поэтому этот метод очень полезен для передачи переменных экземпляра методу класса execute_action
.
Рассмотрим пример.
@hooks.register('register_bulk_action')
class CustomBulkAction(ImageBulkAction):
display_name = _("A Thing")
aria_label = _("A thing to do")
action_type = "thing"
template_name = "appname/bulk/something.html"
def get_execution_context(self):
print(self.request)
return super().get_execution_context()
Если вы запустите этот пример, вы увидите данные, представленные из HTML form
.
<WSGIRequest: POST '/admin/bulk/image/customimage/thing/?next=%2Fadmin%2Fimages%2F&id=1'>
Определение HTML-формы
В шаблоне массового действия вы не можете найти ни одного тега HTML <form></form>
. Это потому, что форма с кнопками действий находится в файле wagtailadmin/bulk_actions/confirmation/form.html
, который вы импортировали в шаблон. Вы можете создать копию этого файла и изменить его поведение.
<form action="{{ submit_url }}" method="POST">
{% include 'wagtailadmin/shared/non_field_errors.html' %}
{% csrf_token %}
{% block form_fields %}
<!-- Custom Fields goes here -->
{% endblock form_fields %}
<input type="submit" value="{{ action_button_text }}" class="button {{ action_button_class }}" />
<a href="{{ next }}" class="button button-secondary">{{ no_action_button_text }}</a>
</form>
Вы можете добавить нужные вам пользовательские поля в области, которую я указал выше в примере кода, и значения этих дополнительных полей будут находиться в параметре self.request.POST
. Это самый простой способ получить что-то из шаблона на стороне python.
Django Forms
Но это не самый лучший способ. Django рекомендует использовать формы для этих целей. Подробнее о формах Django вы можете узнать в документации.
Почти везде, где есть форма в шаблоне wagtail, есть связанная с ней форма Django. В данном случае переменная экземпляра form_class
используется для ассоциации шаблона bulk action с формой Django.
class MyForm(forms.Form):
extra_field = forms.CharField(
max_length=100,
required=True,
)
@hooks.register('register_bulk_action')
class CustomBulkAction(ImageBulkAction):
display_name = _("A Thing")
aria_label = _("A thing to do")
action_type = "thing"
template_name = "appname/bulk/something.html"
form_class = MyForm
def get_execution_context(self):
print(self.cleaned_form.data)
return super().get_execution_context()
И очень просто, я добавлю все поля формы в шаблон, как в приведенном ниже примере кода.
<form action="{{ submit_url }}" method="POST">
{% include 'wagtailadmin/shared/non_field_errors.html' %}
{% csrf_token %}
{% block form_fields %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.label_tag }} {{ field }}
{{ field.errors }}
</div>
{% endfor %}
{% endblock form_fields %}
<input type="submit" value="{{ action_button_text }}" class="button {{ action_button_class }}" />
<a href="{{ next }}" class="button button-secondary">{{ no_action_button_text }}</a>
</form>
Теперь это выведет данные, полученные из HTML-формы. Нам нужно передать данные формы как kwargs
в метод класса execute_action
.
Окончательный пример
@hooks.register('register_bulk_action')
class CustomBulkAction(ImageBulkAction):
display_name = _("A Thing")
aria_label = _("A thing to do")
action_type = "thing"
template_name = "appname/bulk/something.html"
form_class = MyForm
def get_execution_context(self):
data = super().get_execution_context()
data['form'] = self.cleaned_form
return data
@classmethod
def execute_action(cls, objects, **kwargs):
print("KWARGS:", kwargs)
print(kwargs['form'].cleaned_data['extra_field'])
# Do what you want
return 0, 0
Я считаю, что это было полезно и ответило на все вопросы, связанные с массовым представлением действий.
Используя forms.ModelChoiceField
в вашей форме, вы можете получать значения из Django Model
и передавать их в поле HTML. Вы должны передать queryset
в конструкторе.
extra_field = forms.ModelChoiceField(
required=True,
queryset=Collection.objects.order_by("name"),
)