Django Admin Action Function Failing - отправка электронной почты нескольким выбранным пользователям (если «apply» в request.POST)

В моей реализации используется модель CustomUser. Я создаю действие в админке Django, которое позволяет мне составлять и отправлять электронные письма нескольким пользователям. Все на сайте ведет себя так, как будто все работает. После нажатия кнопки «Отправить письмо» я перенаправляюсь обратно в админку, но не вижу никаких консольных выводов, указывающих на то, что письмо было отправлено, и self.message_user никогда не вызывается и не отправляется в админку.

Email Backend

Сброс пароля работает со ссылкой на сброс, отправленной в консоль, так что бэкэнд электронной почты работает.

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

admin.py

if «apply» in request.POST: <-- Эта строка никогда не срабатывает!

class CustomUserAdmin(ExportMixin, UserAdmin):

    @admin.action(description="Email selected users")
    def send_email_action(self, request, queryset):
        print(request.POST)
        print(request.POST.getlist("apply"))
        input("HALT")
        if "apply" in request.POST:
            print("apply")
            form = EmailForm(request.POST)
            if form.is_valid():
                print("form valid")
                subject = form.cleaned_data["subject"]
                message = form.cleaned_data["message"]
                recipients = [user.email for user in queryset]
                send_mail(subject, message, "your_email@example.com", recipients)
                self.message_user(request, f"{len(recipients)} emails were sent.")
                return HttpResponseRedirect(request.get_full_path())
        else:
            print("no apply")
            form = EmailForm()
        return render(request, "email/admin_send.html", {"form": form})

forms.py

Работает, как и ожидалось.

class EmailForm(forms.Form):
    subject = forms.CharField(max_length=256)
    message = forms.CharField(widget=forms.Textarea)

admin_send.html

Либо:

  • Функция send_email_action вызывается после нажатия кнопки «Отправить письмо», но форма не отправляет обратно данные «apply», поэтому она терпит неудачу: if «apply in request.POST:
  • --ИЛИ--
  • Функция send_email_action даже не вызывается снова, потому что я не знаю, как это сделать, возможно, это имеет какое-то отношение к этому: form action=«»

Q1: Нужно ли мне определять form action=«» , которая сейчас пуста? Я спрашиваю, потому что мне кажется, что когда я нажимаю кнопку «Отправить электронную почту», она может не вызывать функцию send_email_action снова. Если мне нужно определить это, я не знаю, что сюда вставить, потому что обычно это URL, но мне нужно, чтобы это была функция действия администратора. Я считаю, что это основная проблема, потому что если функция вызывается, то я должен увидеть результаты print(request.POST), выведенные в консоль, но я этого не делаю (т.е. QueryDict).

Q2: Обратите внимание на input name=«apply». Функция send_email_action явно ищет эти данные, но они не возвращаются из формы, а если и возвращаются, то я не могу найти их в объекте запроса. Я полагаю, что это также может быть проблемой, но мы сможем решить ее только после того, как решим первую проблему выше.

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block title %}
Email Users
{% endblock %}

{% block content %}
<br>
<br>
<br>
<h2>Email Users</h2>
<form action="" method="post">
    {% csrf_token %}
    {{ form|crispy }}
    <input type="submit" name="apply" value="Send Email">
</form>
{% endblock content %}

Шаг 1) В админке я выбрал несколько пользователей в таблице CustomUser, выбрал «Email selected users» из выпадающего меню действий, нажал GO:

console

<QueryDict: {'csrfmiddlewaretoken': ['zHlbmtRaNIFwrdNmCFxwyiVLi1IaCFKHoJAmZWKttwTMN97gC1JhWSryO9POuaDK'], 'action': ['send_email_action'], 'select_across': ['0'], 'index': ['0'], '_selected_action': ['6', '1', '7', '8']}>
[]
HALT

Шаг2) Я нажимаю enter в консоли, чтобы продолжить, появляется форма. Я ввожу тему и сообщение.

console

no apply
[23/Nov/2024 23:58:25] "POST /admin/app_accounts/customuser/?company=Cisco HTTP/1.1" 200 10317

Шаг 3) Я нажимаю кнопку «Отправить письмо».

console

[24/Nov/2024 00:02:07] "POST /admin/app_accounts/customuser/?company=Cisco HTTP/1.1" 200 19098
[24/Nov/2024 00:02:07] "GET /admin/jsi18n/ HTTP/1.1" 200 3342

ОК, я понял, как заставить это работать. Ключевым моментом для меня было просто осознание того, что form action=«» на самом деле должна быть заполнена. Чем? Я не знал, как заполнить ее чем-то, что позволило бы мне вернуться к моей функции действия администратора <- это был мой ментальный блок. ДУХ! Мне нужно было заполнить его обычным представлением, а не функцией действий администратора! Мне нужно было разделить эти части кода. Поэтому я создал URL, по которому можно было отправлять POST-данные формы, придумал, как прозрачно передать данные из моей функции действия администратора через промежуточную форму в обычное представление для последующей обработки (немного преобразования данных), затем я построил свое представление, используя части кода из моего оригинального файла admin.py в разделе if «apply» in request.POST:. Затем я упростил свою функцию действия администратора.

Единственное, что я не смог заставить работать, - это сообщение о подтверждении в интерфейс администратора... см. комментарий # в предпоследней строке моего файла views.py. Это привело к ошибке: ModelAdmin.message_user() missing 1 required positional argument: 'message'. Хотя ясно, что я указал сообщение. Я не могу объяснить, почему это происходит.

Это решение кажется мне немного грязным, потому что прозрачная передача данных, кажется, передается в виде параметров в URL, но ничего конфиденциального не передается, так что я думаю, что это не так уж важно, и это никогда не станет известно конечному пользователю, пока код работает правильно.

Если кто-нибудь прочитает это, я надеюсь, что это поможет. Также, если вы можете придумать лучший, более простой, более эффективный способ решения этой проблемы, дайте мне знать! Я довольно новичок в Django, поэтому мне приятно, что у меня вообще получилось это сделать, но я задаюсь вопросом, нет ли лучшего способа.

admin.py

class CustomUserAdmin(ExportMixin, UserAdmin):
    resource_class = CustomUserResource
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    actions = ["send_email_action"]

    @admin.action(description="Email selected users")
    def send_email_action(self, request, queryset):
        form = EmailForm()
        recipients = "|".join([item.email for item in queryset])
        return render(
            request,
            "email/admin_send.html",
            {"form": form, "recipients": recipients},

forms.py

class EmailForm(forms.Form):
    subject = forms.CharField(max_length=256)
    message = forms.CharField(widget=forms.Textarea)

admin_send.html

{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block title %}
Email Users
{% endblock %}

{% block content %}
<br>
<br>
<br>
<h2>Email Users</h2>
<form action="{% url 'process_email' recipients %}" method="post">
    {% csrf_token %}
    {{ form|crispy }}
    <input type="submit" value="Send Email">
</form>
{% endblock content %}

urls.py

from django.urls import path
from .views import process_email_view

urlpatterns = [
    path(
        "process_email/<str:recipients>/",
        process_email_view,
        name="process_email",
    ),
]

views.py

def process_email_view(request, recipients, modeladmin):
    form = EmailForm(request.POST)
    if form.is_valid():
        subject = form.cleaned_data["subject"]
        message = form.cleaned_data["message"]
        recipients = recipients.split("|")
        for recipient in recipients:
            send_mail(subject, message, "your_email@example.com", [recipient])
        # CustomUserAdmin.message_user(request, f"{len(recipients)} emails were sent.")
        return HttpResponseRedirect("/admin/app_accounts/customuser/")

Вернуться на верх