You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django使用asyncio异步发邮件报SynchronousOnlyOperation错误如何解决

问题原因

从错误栈可以直接定位,报错触发在代码第一行if request.user.profile.role == 'sa' or request.user.profile.role == 'e':的位置,并非你后续发邮件、查询用户的步骤出错。Django的request.user是SimpleLazyObject懒加载对象,首次访问时会同步查询session数据库加载用户数据,异步上下文内直接调用同步数据库操作就会抛出SynchronousOnlyOperation错误。

需要用sync_to_async包裹的操作

所有同步IO相关的操作都需要用sync_to_async包裹,包括但不限于:

  • 所有数据库读写操作:ORM查询、表单save操作、懒加载对象的首次访问
  • Django内置同步方法:messages.success/warning、render_to_string、redirect等
  • 邮件发送、文件IO等其他同步阻塞操作

你代码里查询User、包裹邮件发送的操作是正确的,错误是遗漏了其他同步操作的异步适配。

修正后的代码示例
from asgiref.sync import sync_to_async
import asyncio

async def new(request):
    # 异步加载用户角色,避免懒加载触发同步查询
    def get_user_role():
        return request.user.profile.role
    role = await sync_to_async(get_user_role)()
    
    if role == 'sa' or role == 'e':
        if request.method == "GET":
            raise Http404

        elif request.method == "POST":
            form = TaskForm(request.POST)
            # 表单验证是同步操作,需包裹
            is_valid = await sync_to_async(form.is_valid)()
            if is_valid:
                # 表单保存是同步数据库操作,需包裹
                task = await sync_to_async(form.save)()
                
                subject = "New Task"
                # 模板渲染是同步操作,需包裹
                def render_email_content():
                    return render_to_string('new_task_email.html', {'task': task})
                message = await sync_to_async(render_email_content)()
                
                user = await sync_to_async(User.objects.get)(id=task.assign_to.id)
                send_mail_async = sync_to_async(user.email_user)
                asyncio.create_task(send_mail_async(subject, message, EMAIL_HOST_USER))

                # messages相关方法是同步操作,需包裹
                await sync_to_async(messages.success)(request, "New Task has ceated")
            else:
                await sync_to_async(messages.warning)(request, "Please Enter a valid data")
            # redirect是同步操作,需包裹
            return await sync_to_async(redirect)('tasks')
    else:
        raise Http404
额外注意点

使用asyncio.create_task发送邮件时需要注意,若异步视图在邮件任务执行完成前就返回,部分部署环境会销毁请求上下文导致邮件发送失败,若出现该问题可以换成await等待邮件发送完成,或者接入专门的异步任务队列(如Celery)处理邮件类异步任务。

内容的提问来源于stack exchange,提问作者Maninder Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 06:18:00