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

Django项目中如何传递request至Celery任务参数?PDF生成邮件咨询

问题解答:Django中传递Request给Celery任务 + wkhtmltopdf生成PDF异步发邮件

一、为什么不能直接传Request给Celery?

先给你说清楚核心原因:Django的request对象是没法直接序列化的,Celery任务要求参数必须是可被JSON序列化的类型,但request里包含了大量不可序列化的属性(比如WSGI连接对象、请求上下文状态等),直接传肯定会报错。所以正确的思路是提取request里的关键信息,把这些可序列化的信息传给Celery任务,而非整个request对象。

具体实现方式

举个实际场景的例子:如果你需要在任务里用到当前用户、语言偏好或者请求里的特定参数,可以这么做:

  1. 在视图里提取必要信息,触发任务:
# 你的视图代码中
def get(self, request, **kwargs):
    # 提取关键信息
    user_id = request.user.id if request.user.is_authenticated else None
    language = request.LANGUAGE_CODE
    challan_id = kwargs['pk']
    
    # 调用Celery任务,传提取后的信息
    generate_challan_pdf_email.delay(challan_id, user_id, language)
    
    # 快速返回响应,不用等任务执行完
    return HttpResponse("Challan邮件正在后台发送,请注意查收")
  1. 在Celery任务里,用这些信息重构你需要的上下文:
from celery import shared_task
from django.contrib.auth.models import User
from django.utils.translation import activate

@shared_task
def generate_challan_pdf_email(challan_id, user_id, language):
    # 恢复语言环境(如果有国际化需求)
    activate(language)
    # 通过user_id获取用户对象(如果任务里需要用到)
    user = User.objects.get(id=user_id) if user_id else None
    
    # 后续处理你的PDF生成和邮件逻辑...

如果确实需要用到session数据,可以传递request.session.session_key,然后在任务里通过SessionStore获取session内容,但要注意session的有效期问题,避免任务执行时session已经过期。


二、结合wkhtmltopdf的异步PDF生成+邮件发送优化

从你给出的视图代码来看,当前是同步处理PDF生成和邮件发送,这会让用户等待很久。我们把这部分耗时逻辑移到Celery任务里,让视图快速返回,后台异步处理。

步骤1:修改视图,只负责触发任务

class ChallanEmail(AtomicMixin, View, LoginRequiredMixin):
    template = "europarts/challan/email_template.html"

    def get(self, request, **kwargs):
        challan_id = kwargs['pk']
        # 提取request里的必要信息
        user_id = request.user.id
        language = request.LANGUAGE_CODE
        
        # 触发异步任务
        generate_challan_pdf_email.delay(challan_id, user_id, language)
        
        # 返回友好提示,或者重定向到列表页
        return HttpResponse("邮件正在后台处理,稍后会发送到收件箱")

步骤2:编写Celery任务处理核心逻辑

from celery import shared_task
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.utils.translation import activate
import tempfile
import pdfkit
import os

@shared_task(bind=True, retry_backoff=3)
def generate_challan_pdf_email(self, challan_id, user_id, language):
    try:
        # 激活对应语言环境
        activate(language)
        # 获取用户对象(如果任务中需要用到)
        user = User.objects.get(id=user_id)
        
        # 查询Challan相关数据
        challan = Challan.objects.get(pk=challan_id)
        ref_no = challan.ref_no
        date = challan.created
        recipient = challan.recipient
        address = challan.recipient_address
        challan_rows = ChallanRow.objects.filter(challan=challan)
        
        # 构建模板上下文
        context = {
            "ref_no": ref_no,
            "date": date,
            "recipient": recipient,
            "address": address,
            "challan_rows": challan_rows,
            "user": user
        }
        
        # 渲染HTML模板
        html_content = render_to_string("europarts/challan/email_template.html", context)
        
        # 用wkhtmltopdf生成PDF(用临时文件存储)
        with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as temp_pdf:
            # 注意:要指定wkhtmltopdf的实际路径,避免worker找不到
            config = pdfkit.configuration(wkhtmltopdf='/usr/bin/wkhtmltopdf')
            pdfkit.from_string(html_content, temp_pdf.name, configuration=config)
            
            # 准备并发送邮件
            email = EmailMessage(
                subject=f"Challan 单据 #{ref_no}",
                body="您好,附件是您的Challan单据,请查收。",
                from_email="your-service@example.com",
                to=[recipient],
            )
            email.attach_file(temp_pdf.name, mimetype='application/pdf')
            email.send()
        
        # 清理临时文件
        os.unlink(temp_pdf.name)
    except Challan.DoesNotExist:
        # 如果Challan被删除,标记任务失败,不重试
        self.retry(max_retries=0)
    except Exception as e:
        # 其他异常重试3次,间隔3秒
        self.retry(exc=e, max_retries=3)

关键注意点

  • 确保Celery worker能加载Django配置:启动worker时要设置环境变量export DJANGO_SETTINGS_MODULE=your_project.settings。
  • wkhtmltopdf必须在worker所在服务器上安装好,并且在代码中指定正确的路径(比如上面的/usr/bin/wkhtmltopdf,根据你的实际安装路径调整)。
  • 任务中处理数据库对象时,最好重新查询最新数据,避免视图传递过来的对象已经被修改。
  • 配置了重试机制,确保临时网络问题导致的发送失败能自动重试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:45:45