Django集成SendGrid邮件订阅遇401未授权及唯一约束错误排查
问题排查与解决方法
你遇到了两个独立的问题:唯一约束冲突错误和SendGrid未授权错误,我来逐一帮你分析解决:
一、django.db.utils.IntegrityError: UNIQUE constraint failed: home_subscriber.email
原因
你的Subscriber模型中email字段设置了unique=True,数据库不允许存在重复的邮箱记录。当用户重复提交同一个邮箱时,你的代码直接创建新的Subscriber实例并调用save(),就会触发这个唯一约束冲突。
解决方法
修改subscribe视图,在创建新实例前先检查邮箱是否已存在,根据不同情况处理:
import random from django.shortcuts import render from django.views.decorators.csrf import csrf_exempt from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from .models import Subscriber from .forms import SubscriberForm from django.conf import settings def random_digits(): return "%0.12d" % random.randint(0, 999999999999) @csrf_exempt def subscribe(request): if request.method == 'POST': email = request.POST['email'] # 尝试获取已存在的订阅记录,不存在则创建 sub, created = Subscriber.objects.get_or_create(email=email) if not created: # 邮箱已存在的情况 if sub.confirmed: return render(request, 'sub.html', { 'email': email, 'action': 'already_confirmed', 'form': SubscriberForm() }) else: # 未确认,重新生成确认码 sub.conf_num = random_digits() sub.save() else: # 新创建的订阅记录,设置确认码 sub.conf_num = random_digits() sub.save() # 统一发送确认邮件 message = Mail( from_email=settings.FROM_EMAIL, to_emails=sub.email, subject='Newsletter Confirmation', html_content='Thank you for signing up for my email newsletter! \ Please complete the process by \ <a href="{}/confirm/?email={}&conf_num={}"> clicking here to \ confirm your registration</a>.'.format(request.build_absolute_uri('/confirm/'), sub.email, sub.conf_num)) try: sg = SendGridAPIClient(settings.SENDGRID_API_KEY) response = sg.send(message) except Exception as e: return render(request, 'sub.html', { 'email': sub.email, 'action': 'send_failed', 'form': SubscriberForm() }) action = 'added' if created else 'resend_confirmation' return render(request, 'sub.html', { 'email': sub.email, 'action': action, 'form': SubscriberForm() }) else: return render(request, 'sub.html', {'form': SubscriberForm()})
这段代码用get_or_create避免重复创建记录,同时处理两种场景:已确认的邮箱直接提示用户,未确认的邮箱重新发送确认邮件。
二、python_http_client.exceptions.UnauthorizedError: HTTP Error 401: Unauthorized
原因
这个错误是SendGrid拒绝了你的API请求,常见触发原因有:
- API密钥未正确加载:环境变量
SENDGRID_API_KEY未配置或加载失败,导致请求时没有携带有效密钥。 - 发件邮箱未验证:你设置的
FROM_EMAIL没有在SendGrid控制台完成验证(单个邮箱或域名验证),SendGrid会拦截未验证的发件请求。 - API密钥权限不足:创建密钥时未授予
Mail Send权限,导致无法调用邮件发送接口。
解决步骤
- 确认API密钥是否有效加载:
在视图中临时添加打印语句,检查密钥是否获取到:
如果输出为空,说明环境变量配置有问题,可以临时在print("Loaded SendGrid API Key:", settings.SENDGRID_API_KEY)settings.py中硬编码密钥测试(测试完成后务必改回环境变量方式):SENDGRID_API_KEY = "你的真实SendGrid API密钥" - 验证发件邮箱:
登录SendGrid控制台,进入Settings > Sender Authentication,选择Single Sender Verification,添加并验证你的FROM_EMAIL地址。 - 检查API密钥权限:
进入SendGrid的Settings > API Keys,找到你使用的密钥,确保它拥有Mail Send的权限(可以设置为Full Access或仅勾选Mail Send的Write权限)。
额外优化建议
- 移除
csrf_exempt装饰器,改用Django表单的CSRF保护机制,提升网站安全性。 - 在前端表单添加邮箱重复提交的验证(比如用AJAX提前检查邮箱是否已存在),减少后端错误触发。
- 所有邮件发送操作都包裹在
try-except块中,避免因邮件服务故障导致整个请求崩溃。
内容的提问来源于stack exchange,提问作者Eman5648
相关产品推荐
相关产品推荐

