Django Admin批量用户邮件动作失效:点击发送无响应排查
我基于CustomUser模型开发了一个Django Admin自定义动作,用来给选中的多个用户发送邮件。页面表面行为正常,但点击「Send Email」按钮后,直接回到Admin界面,控制台没有邮件发送记录,self.message_user的通知也没触发。
邮箱后端状态
密码重置功能可以正常把链接输出到控制台,说明EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"配置是有效的。
代码问题点
admin.py 核心问题
if "apply" in request.POST: 语句始终未执行:
class CustomUserAdmin(ExportMixin, UserAdmin): @admin.action(description="给选中用户发邮件") 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)} 封邮件。") return HttpResponseRedirect(request.get_full_path()) else: print("no apply") form = EmailForm() return render(request, "email/admin_send.html", {"form": form})
forms.py 状态
EmailForm功能正常:
class EmailForm(forms.Form): subject = forms.CharField(max_length=256) message = forms.CharField(widget=forms.Textarea)
admin_send.html 疑似问题
- 点击「Send Email」后,
send_email_action函数未被再次调用,可能与form action=""为空有关; - 表单中
input name="apply"的参数未传递到request对象中。
{% extends "base.html" %} {% load crispy_forms_tags %} {% block title %} 给用户发邮件 {% endblock %} {% block content %} <br> <br> <br> <h2>给用户发邮件</h2> <form action="" method="post"> {% csrf_token %} {{ form|crispy }} <input type="submit" name="apply" value="Send Email"> </form> {% endblock content %}
操作步骤与控制台输出
- 在Admin中选中多个CustomUser,选择「给选中用户发邮件」动作并点击GO:
<QueryDict: {'csrfmiddlewaretoken': ['zHlbmtRaNIFwrdNmCFxwyiVLi1IaCFKHoJAmZWKttwTMN97gC1JhWSryO9POuaDK'], 'action': ['send_email_action'], 'select_across': ['0'], 'index': ['0'], '_selected_action': ['6', '1', '7', '8']}> [] HALT
- 回车后表单显示,输入主题和消息:
no apply [23/Nov/2024 23:58:25] "POST /admin/app_accounts/customuser/?company=Cisco HTTP/1.1" 200 10317
- 点击「Send Email」按钮:
[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
疑问
Q1:是否需要设置form action?如果需要,应该配置什么值才能再次触发send_email_action函数?
Q2:如何确保input name="apply"的参数能被正确传递到request对象中?
解决方案
解决Q1:让表单提交触发动作函数
要让点击「发送邮件」时再次调用send_email_action,需要在表单中保留Admin动作的核心参数(action、_selected_action等),同时把表单的action设为当前Admin页面的完整路径。
修改admin_send.html,添加隐藏字段传递关键参数:
{% extends "base.html" %} {% load crispy_forms_tags %} {% block title %} 给用户发邮件 {% endblock %} {% block content %} <br> <br> <br> <h2>给用户发邮件</h2> <form action="{{ request.get_full_path }}" method="post"> {% csrf_token %} <!-- 隐藏字段:传递Admin动作必要参数,确保触发send_email_action --> <input type="hidden" name="action" value="{{ request.POST.action }}"> {% for user_id in request.POST._selected_action %} <input type="hidden" name="_selected_action" value="{{ user_id }}"> {% endfor %} {% if request.POST.select_across %} <input type="hidden" name="select_across" value="{{ request.POST.select_across }}"> {% endif %} {% if request.POST.index %} <input type="hidden" name="index" value="{{ request.POST.index }}"> {% endif %} {{ form|crispy }} <input type="submit" name="apply" value="发送邮件"> </form> {% endblock content %}
表单提交时,会把触发send_email_action所需的参数一起POST到Admin页面,Django Admin会自动调用对应的动作函数。
解决Q2:确保apply参数正确传递
只要表单method为post,且提交按钮的name="apply"无拼写错误,参数就会被包含在POST请求中。上述表单修改已满足条件,提交后request.POST中会出现apply键,if "apply" in request.POST:即可正常执行。
额外注意点
- 无需手动处理查询集:第二次调用
send_email_action时,Django Admin会根据_selected_action参数自动生成正确的queryset,直接使用即可; - 测试时可在
send_email_action中打印request.POST,确认apply和其他参数是否存在。
内容的提问来源于stack exchange,提问作者Dan

