Django中测试Amazon SES邮件发送失败:邮件未实际发出
Django中测试Amazon SES邮件发送失败:邮件未实际发出
遇到这种线上正常但测试环境发不出邮件的问题确实头疼,我来帮你梳理几个可能的排查方向和解决方案:
1. 先排查测试环境的AWS SES权限与配置问题
- 验证IAM用户权限:你测试环境用的IAM用户是否拥有SES的
ses:SendEmail或ses:SendRawEmail权限?线上可能用的是EC2实例角色,权限自动继承,但测试环境的独立IAM用户可能没配置这个权限,导致发送请求被AWS拒绝。 - 检查SES沙盒模式限制:如果你的SES账号还在沙盒模式下,只能发送到已验证的邮箱地址。确认
settings.NOTIFICATIONS_TESTERS里的收件邮箱都在AWS SES控制台完成了验证,不然邮件会被悄悄拦截,不会抛出异常但也不会实际发出。 - 确认区域配置一致性:你配置的区域是
ca-central-1,要确保测试环境用的IAM用户、验证的邮箱都属于这个区域,SES的资源是区域隔离的。
2. 确认测试环境的邮件后端未被覆盖
很多Django项目会在测试配置(比如settings/test.py)里把邮件后端替换成内存或控制台后端(django.core.mail.backends.locmem.EmailBackend或console.EmailBackend),来避免实际发邮件。你可以在测试代码里加一行打印确认:
print("当前邮件后端:", settings.EMAIL_BACKEND)
如果输出不是django_ses.SESBackend,那就是测试配置覆盖了线上的设置,需要调整测试环境的配置。
3. 调整连接对象的初始化与使用方式
你当前用mail.get_connection()获取连接,虽然理论上没问题,但可以试试显式初始化SESBackend来确保配置正确加载:
from django_ses import SESBackend connection = SESBackend( aws_access_key_id=settings.AWS_ACCESS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, region_name=settings.AWS_DEFAULT_REGION )
同时,记得把这个连接对象传给每个EmailMessage,或者确保send_messages用的是这个正确的连接。另外,发送完成后手动关闭连接:
connection.send_messages(emails) connection.close()
4. 优化信号与断言的时机
- 信号注册时机:你在测试函数里才连接信号,有可能在创建
EmailMessage之后才完成注册,导致信号没触发。建议把信号的连接和断开放到测试类的setUp和tearDown方法里:def setUp(self): signals.message_sent.connect(_mail_signal_assertion_handler) def tearDown(self): signals.message_sent.disconnect(_mail_signal_assertion_handler) - SES统计数据延迟问题:SES的发送统计数据不是实时更新的,你发送邮件后立刻调用
get_ses_emails_data,大概率还没拿到最新的统计结果,导致断言失败。可以临时加个短暂等待(虽然测试里用sleep不太优雅,但针对SES这个特例是可行的):
或者更靠谱的方式:放弃依赖SES的API统计,转而检查import time connection.send_messages(emails) time.sleep(3) # 等待SES更新统计 post_data = get_ses_emails_data()connection.send_messages的返回值(它会返回成功发送的邮件数量),这样能直接验证发送结果:sent_count = connection.send_messages(emails) assert sent_count == send_ct
5. 开启日志排查隐藏错误
开启Django的DEBUG模式,或者配置日志记录django_ses和boto3的详细日志,看看有没有隐藏的错误信息。比如在发送邮件时捕获异常:
try: sent_count = connection.send_messages(emails) print(f"成功发送{sent_count}封邮件") except Exception as e: import traceback traceback.print_exc()
很多时候AWS会返回详细的错误原因(比如权限不足、邮箱未验证),但django-ses可能不会主动抛出,需要手动捕获查看。
备注:内容来源于stack exchange,提问作者Marc
相关产品推荐
相关产品推荐

