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

Django多进程发邮件报错:AttributeError无法序列化本地对象

解决Django多进程发送邮件时的Pickle序列化错误

嘿,我来帮你搞定这个问题!你遇到的AttributeError: Can't pickle local object错误,本质是Windows系统下multiprocessing的工作机制导致的,结合你的代码来看,主要有两个核心问题:

错误根源分析

  1. 局部函数无法被Pickle序列化:Windows下multiprocessing默认使用spawn模式启动子进程,这个模式需要把要执行的函数和参数序列化(通过pickle)后传递给子进程。但你把send函数定义在了handle方法内部,属于局部函数,pickle无法识别这种不在模块全局命名空间里的函数,所以报错。
  2. 跨进程共享邮件连接不安全:你在主进程创建了一个connection对象然后让所有子进程共用,邮件/数据库连接这类资源是和进程绑定的,跨进程共享会导致连接状态混乱,就算没序列化错误,运行起来也大概率出问题。

修复方案

我给你调整了代码,解决了这两个问题:

from django.core.management.base import BaseCommand
from django.core.mail import get_connection, EmailMessage
from django.template.loader import get_template
from multiprocessing import Pool
from sending.models import Subscriber

class Command(BaseCommand):
    @staticmethod
    def send_single_email(email, subject, body_content, from_addr):
        # 每个子进程独立创建邮件连接,避免跨进程共享问题
        with get_connection() as connection:
            msg = EmailMessage(subject, body_content, from_addr, [email])
            msg.content_subtype = 'html'
            print(f"Sending email to: {email}")
            connection.send_messages([msg])
            print(f"Successfully sent to {email}")

    def handle(self, *args, **options):
        # 提前获取所有收件人、渲染邮件内容(主进程做一次就行)
        recipients = [sub.email for sub in Subscriber.objects.all()]
        subject = 'Multiprocessing trying 1'
        body = get_template('Newsletter20171229.html').render()
        from_email = 'info@modoodoc.com'

        # 创建进程池
        with Pool(processes=2) as pool:
            # 用starmap传递多个参数给子进程的函数
            pool.starmap(
                self.send_single_email,
                [(email, subject, body, from_email) for email in recipients]
            )

        print("All emails have been dispatched. Program exiting.")

关键调整点说明

  • 把发送函数移到类的静态方法:send_single_email是Command类的静态方法,属于模块级可访问的函数,pickle可以正常序列化它。
  • 每个子进程独立创建连接:用with get_connection()上下文管理器,每个子进程自己创建和关闭连接,避免共享资源的冲突。
  • 使用starmap传递多参数:因为要给send函数传多个参数(邮件地址、主题、内容、发件人),starmap可以把元组里的元素作为单独参数传递给函数,比map更灵活。
  • 用with管理进程池:自动帮你关闭进程池,不用手动调用pool.close()或pool.join(),更简洁安全。

如果你不想用静态方法,也可以把send_single_email移到模块最外层(Command类外面),效果是一样的,只要它是模块全局可访问的函数就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:47:37