使用Celery发送Djoser重置密码邮件时的序列化问题求解
解决Djoser重置密码异步邮件的Request序列化问题
核心问题是不能直接传递整个Django Request对象给Celery任务,因为Request包含大量不可序列化的属性(如IO流、底层连接实例),不管用JSON还是pickle都无法处理。正确的做法是从Request中提取任务必需的信息,只传递这些序列化友好的数据。
具体解决方案步骤
1. 重写reset_password方法,提取关键参数
在自定义的UserViewSet中,不要把Request传给Celery任务,而是提取出邮件发送需要的核心数据:
from djoser.views import UserViewSet from celery import shared_task class CustomUserViewSet(UserViewSet): def reset_password(self, request, *args, **kwargs): # 执行原逻辑完成密码重置核心操作 response = super().reset_password(request, *args, **kwargs) # 从请求中提取必要信息,而非传递整个request user = self.get_object() site_domain = request.get_host() protocol = 'https' if request.is_secure() else 'http' language_code = request.LANGUAGE_CODE # 调用Celery任务,传递提取后的参数 send_reset_password_email.delay( user_id=user.id, site_domain=site_domain, protocol=protocol, language_code=language_code ) return response
2. 编写Celery任务,基于提取的参数生成邮件
任务中通过user_id重新获取用户实例,再用提取的域名、协议等信息构建重置链接:
from django.contrib.auth import get_user_model from django.core.mail import send_mail from django.utils.translation import activate from djoser import utils User = get_user_model() @shared_task def send_reset_password_email(user_id, site_domain, protocol, language_code): # 激活对应语言环境,保证邮件内容本地化 activate(language_code) user = User.objects.get(id=user_id) # 生成重置密码令牌 token = utils.generate_token(user) # 构建重置链接 reset_url = f"{protocol}://{site_domain}/password/reset/confirm/{user.id}/{token}/" # 发送邮件(可复用Djoser内置邮件模板逻辑) send_mail( subject="重置你的密码", message=f"点击链接重置密码:{reset_url}", from_email=None, recipient_list=[user.email], fail_silently=False, )
关键说明
- Request对象包含
_io.BufferedReader这类无法被pickle序列化的IO实例,同时JSON序列化也无法处理复杂的Django对象结构,因此绝对不能直接传递。 - 提取的参数都是字符串、整数这类基础类型,天然支持JSON序列化,完全符合Celery的任务参数要求。
- 如果需要复用Djoser的邮件模板逻辑,可以直接调用Djoser内置的邮件发送工具类,只需要传入必要的上下文数据即可。
内容的提问来源于stack exchange,提问作者lev_k
相关产品推荐
相关产品推荐

