发送电子邮件 — Django 文档

来自菜鸟教程
Django/docs/3.2.x/topics/email
跳转至:导航、​搜索

发送电子邮件

尽管 Python 通过 smtplib 模块提供了一个邮件发送接口,但 Django 提供了几个轻量级的包装器。 提供这些包装器是为了使发送电子邮件更加快速,帮助在开发过程中测试电子邮件发送,并为不能使用 SMTP 的平台提供支持。

代码位于 django.core.mail 模块中。

快速示例

在两行中:

from django.core.mail import send_mail

send_mail(
    'Subject here',
    'Here is the message.',
    'from@example.com',
    ['to@example.com'],
    fail_silently=False,
)

使用 :setting:`EMAIL_HOST`:setting:`EMAIL_PORT` 设置中指定的 SMTP 主机和端口发送邮件。 :setting:`EMAIL_HOST_USER`:setting:`EMAIL_HOST_PASSWORD` 设置(如果设置)用于对 SMTP 服务器进行身份验证,而 :setting:`EMAIL_USE_TLS `:setting:`EMAIL_USE_SSL` 设置控制是否使用安全连接。

笔记

使用 django.core.mail 发送的电子邮件的字符集将设置为您的 :setting:`DEFAULT_CHARSET` 设置的值。


send_mail()

send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)

在大多数情况下,您可以使用 django.core.mail.send_mail() 发送电子邮件。

需要 subjectmessagefrom_emailrecipient_list 参数。

  • subject:一个字符串。
  • message:一个字符串。
  • from_email:一个字符串。 如果是 None,Django 将使用 :setting:`DEFAULT_FROM_EMAIL` 设置的值。
  • recipient_list:字符串列表,每个字符串都是一个电子邮件地址。 recipient_list 的每个成员将在电子邮件的“收件人:”字段中看到其他收件人。
  • fail_silently:布尔值。 当它是 False 时,如果发生错误,send_mail() 将引发 smtplib.SMTPException。 有关可能的异常列表,请参阅 smtplib 文档,所有这些都是 SMTPException 的子类。
  • auth_user:用于向 SMTP 服务器进行身份验证的可选用户名。 如果没有提供,Django 将使用 :setting:`EMAIL_HOST_USER` 设置的值。
  • auth_password:用于向 SMTP 服务器进行身份验证的可选密码。 如果没有提供,Django 将使用 :setting:`EMAIL_HOST_PASSWORD` 设置的值。
  • connection:用于发送邮件的可选电子邮件后端。 如果未指定,将使用默认后端的实例。 有关更多详细信息,请参阅有关 电子邮件后端 的文档。
  • html_message:如果提供 html_message,则生成的电子邮件将是 multipart/alternative 电子邮件,其中 message 作为 text/plain ] 内容类型和 html_message 作为 text/html 内容类型。

返回值将是成功发送的消息数(可以是 01,因为它只能发送一条消息)。


send_mass_mail()

send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)

django.core.mail.send_mass_mail() 用于处理大量电子邮件。

datatuple 是一个元组,其中每个元素都采用以下格式:

(subject, message, from_email, recipient_list)

fail_silentlyauth_userauth_passwordsend_mail() 具有相同的功能。

datatuple 的每个单独元素都会生成单独的电子邮件消息。 与在 send_mail() 中一样,同一 recipient_list 中的收件人都将在电子邮件的“收件人:”字段中看到其他地址。

例如,以下代码将向两组不同的收件人发送两条不同的消息; 但是,只会打开一个到邮件服务器的连接:

message1 = ('Subject here', 'Here is the message', 'from@example.com', ['first@example.com', 'other@example.com'])
message2 = ('Another Subject', 'Here is another message', 'from@example.com', ['second@test.com'])
send_mass_mail((message1, message2), fail_silently=False)

返回值将是成功传递的消息数。

send_mass_mail() 对比 send_mail()

send_mass_mail()send_mail() 的主要区别在于 send_mail() 每次执行时都会打开一个到邮件服务器的连接,而 send_mass_mail() 对其所有消息使用单个连接。 这使得 send_mass_mail() 稍微更有效率。


mail_admins()

mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)

django.core.mail.mail_admins() 是向站点管理员发送电子邮件的快捷方式,如 :setting:`ADMINS` 设置中所定义。

mail_admins() 使用 :setting:`EMAIL_SUBJECT_PREFIX` 设置的值作为主题前缀,默认为 "[Django] "

电子邮件的“发件人:”标题将是 :setting:`SERVER_EMAIL` 设置的值。

这种方法的存在是为了方便和可读性。

如果提供 html_message,则生成的电子邮件将是 multipart/alternative 电子邮件,其中 message 作为 text/plain 内容类型和 html_message 作为 text/html 内容类型。


mail_managers()

mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)

django.core.mail.mail_managers() 就像 mail_admins(),除了它会向站点管理员发送电子邮件,如 :setting:`MANAGERS` 设置中所定义。


例子

这会向 john@example.comjane@example.com 发送一封电子邮件,他们都出现在“收件人:”中:

send_mail(
    'Subject',
    'Message.',
    'from@example.com',
    ['john@example.com', 'jane@example.com'],
)

这会向 john@example.comjane@example.com 发送一条消息,他们都会收到一封单独的电子邮件:

datatuple = (
    ('Subject', 'Message.', 'from@example.com', ['john@example.com']),
    ('Subject', 'Message.', 'from@example.com', ['jane@example.com']),
)
send_mass_mail(datatuple)

防止头部注入

Header injection 是一种安全漏洞,攻击者在其中插入额外的电子邮件标头以控制脚本生成的电子邮件消息中的“To:”和“From:”。

上面概述的 Django 电子邮件函数都通过禁止标题值中的换行符来防止标题注入。 如果任何 subjectfrom_emailrecipient_list 包含换行符(Unix、Windows 或 Mac 风格),则电子邮件功能(例如 send_mail()) 将引发 django.core.mail.BadHeaderErrorValueError 的子类),因此不会发送电子邮件。 您有责任在将所有数据传递给电子邮件功能之前对其进行验证。

如果 message 在字符串的开头包含标题,则标题将作为电子邮件消息的第一位打印。

这是一个示例视图,它从请求的 POST 数据中获取 subjectmessagefrom_email,将其发送到 admin@example.com 并重定向到“/contact/thanks/” 完成后:

from django.core.mail import BadHeaderError, send_mail
from django.http import HttpResponse, HttpResponseRedirect

def send_email(request):
    subject = request.POST.get('subject', '')
    message = request.POST.get('message', '')
    from_email = request.POST.get('from_email', '')
    if subject and message and from_email:
        try:
            send_mail(subject, message, from_email, ['admin@example.com'])
        except BadHeaderError:
            return HttpResponse('Invalid header found.')
        return HttpResponseRedirect('/contact/thanks/')
    else:
        # In reality we'd use a form class
        # to get proper validation errors.
        return HttpResponse('Make sure all fields are entered and valid.')

EmailMessage 类

Django 的 send_mail()send_mass_mail() 函数实际上是利用 EmailMessage 类的瘦包装器。

并非 EmailMessage 类的所有功能都可以通过 send_mail() 和相关的包装函数获得。 如果您希望使用高级功能,例如密件抄送收件人、文件附件或多部分电子邮件,则需要直接创建 EmailMessage 实例。

笔记

这是一个设计特点。 send_mail() 及相关函数原本是 Django 提供的唯一接口。 然而,随着时间的推移,他们接受的参数列表正在缓慢增长。 为电子邮件消息转向更加面向对象的设计并保留原始功能只是为了向后兼容是有意义的。


EmailMessage 负责创建电子邮件本身。 电子邮件后端 然后负责发送电子邮件。

为方便起见,EmailMessage 提供了一个 send() 方法来发送单个电子邮件。 如果您需要发送多条消息,电子邮件后端 API 提供了一个替代方案

EmailMessage 物体

class EmailMessage

EmailMessage 类使用以下参数初始化(如果使用位置参数,则按照给定的顺序)。 所有参数都是可选的,可以在调用 send() 方法之前随时设置。

  • subject:电子邮件的主题行。
  • body:正文。 这应该是纯文本消息。
  • from_email:发件人地址。 fred@example.com"Fred" <fred@example.com> 形式都是合法的。 如果省略,则使用 :setting:`DEFAULT_FROM_EMAIL` 设置。
  • to:收件人地址列表或元组。
  • bcc:发送电子邮件时在“密件抄送”标头中使用的地址列表或元组。
  • connection:电子邮件后端实例。 如果要对多条消息使用相同的连接,请使用此参数。 如果省略,则在调用 send() 时会创建一个新连接。
  • attachments:要放在邮件上的附件列表。 这些可以是 MIMEBase 实例,也可以是 (filename, content, mimetype) 三元组。
  • headers:放置在消息上的额外标题的字典。 键是标题名称,值是标题值。 由调用者确保标题名称和值采用正确的电子邮件格式。 对应的属性是extra_headers
  • cc:发送电子邮件时“抄送”标题中使用的收件人地址列表或元组。
  • reply_to:发送电子邮件时在“Reply-To”标题中使用的收件人地址列表或元组。

例如:

from django.core.mail import EmailMessage

email = EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to1@example.com', 'to2@example.com'],
    ['bcc@example.com'],
    reply_to=['another@example.com'],
    headers={'Message-ID': 'foo'},
)

该类具有以下方法:

  • send(fail_silently=False) 发送消息。 如果在构建电子邮件时指定了连接,则将使用该连接。 否则,将实例化并使用默认后端的实例。 如果关键字参数 fail_silentlyTrue,则发送消息时引发的异常将被取消。 空的收件人列表不会引发异常。 如果消息发送成功则返回1,否则返回0

  • message() 构造一个 django.core.mail.SafeMIMEText 对象(Python 的 MIMEText 类的子类)或一个 django.core.mail.SafeMIMEMultipart 对象保存要发送的消息。 如果您需要扩展 EmailMessage 类,您可能需要重写此方法以将您想要的内容放入 MIME 对象中。

  • recipients() 返回消息的所有收件人的列表,无论他们记录在 toccbcc 属性中。 这是您在子类化时可能需要覆盖的另一种方法,因为在发送消息时需要将完整的收件人列表告知 SMTP 服务器。 如果您添加另一种方法来指定类中的收件人,则他们也需要从该方法中返回。

  • attach() 创建一个新的文件附件并将其添加到邮件中。 调用attach()有两种方式:

    • 您可以向它传递一个作为 MIMEBase 实例的参数。 这将直接插入到生成的消息中。

    • 或者,您可以传递 attach() 三个参数:filenamecontentmimetypefilename 是将出现在电子邮件中的文件附件的名称,content 是将包含在附件中的数据,mimetype 是可选的 MIME 类型附件。 如果省略 mimetype,将根据附件的文件名猜测 MIME 内容类型。

      例如:

      message.attach('design.png', img_data, 'image/png')

      如果指定 message/rfc822mimetype,它也会接受 django.core.mail.EmailMessageemail.message.Message

      对于以 text/ 开头的 mimetype,内容应为字符串。 二进制数据将使用 UTF-8 解码,如果失败,MIME 类型将更改为 application/octet-stream 并且数据将保持不变。

      此外,message/rfc822 附件将不再是违反 RFC 2046#section-5.2.1 的 base64 编码,这可能会导致显示问题EvolutionThunderbird 中的附件。

  • attach_file() 使用文件系统中的文件创建新附件。 使用要附加的文件的路径以及(可选)用于附件的 MIME 类型来调用它。 如果省略 MIME 类型,则会从文件名中猜测。 你可以这样使用它:

    message.attach_file('/images/weather_map.png')

    对于以 text/ 开头的 MIME 类型,二进制数据的处理方式与 attach() 相同。

发送替代内容类型

在电子邮件中包含多个版本的内容会很有用; 经典示例是同时发送消息的文本和 HTML 版本。 借助 Django 的电子邮件库,您可以使用 EmailMultiAlternatives 类来完成此操作。 EmailMessage 的这个子类有一个 attach_alternative() 方法,用于在电子邮件中包含消息正文的额外版本。 所有其他方法(包括类初始化)直接从 EmailMessage 继承。

要发送文本和 HTML 组合,您可以编写:

from django.core.mail import EmailMultiAlternatives

subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()

默认情况下,EmailMessagebody 参数的 MIME 类型为 "text/plain"。 最好不要管它,因为它保证任何收件人都能够阅读电子邮件,而不管他们的邮件客户端如何。 但是,如果您确信收件人可以处理替代内容类型,则可以使用 EmailMessage 类上的 content_subtype 属性来更改主要内容类型。 主要类型将始终为 "text",但您可以更改子类型。 例如:

msg = EmailMessage(subject, html_content, from_email, [to])
msg.content_subtype = "html"  # Main content is now text/html
msg.send()

电子邮件后端

电子邮件的实际发送由电子邮件后端处理。

电子邮件后端类具有以下方法:

  • open() 实例化一个长期存在的电子邮件发送连接。
  • close() 关闭当前的电子邮件发送连接。
  • send_messages(email_messages) 发送 EmailMessage 对象列表。 如果连接未打开,此调用将隐式打开连接,然后关闭连接。 如果连接已经打开,则在发送邮件后它将保持打开状态。

它也可以用作上下文管理器,它会根据需要自动调用 open()close()

from django.core import mail

with mail.get_connection() as connection:
    mail.EmailMessage(
        subject1, body1, from1, [to1],
        connection=connection,
    ).send()
    mail.EmailMessage(
        subject2, body2, from2, [to2],
        connection=connection,
    ).send()

获取电子邮件后端的实例

django.core.mail 中的 get_connection() 函数返回您可以使用的电子邮件后端实例。

get_connection(backend=None, fail_silently=False, *args, **kwargs)

默认情况下,调用 get_connection() 将返回 :setting:`EMAIL_BACKEND` 中指定的电子邮件后端的实例。 如果您指定 backend 参数,则会实例化该后端的一个实例。

fail_silently 参数控制后端应如何处理错误。 如果 fail_silently 为 True,则电子邮件发送过程中的异常将被静默忽略。

所有其他参数都直接传递给电子邮件后端的构造函数。

Django 附带了几个电子邮件发送后端。 除了 SMTP 后端(默认设置)外,这些后端仅在测试和开发期间有用。 如果您有特殊的邮件发送需求,可以【X56X】编写自己的邮件后台【X88X】。

SMTP 后端

class backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs)

这是默认后端。 电子邮件将通过 SMTP 服务器发送。

如果参数为 None,则从匹配设置中检索每个参数的值:

SMTP 后端是 Django 继承的默认配置。 如果您想明确指定它,请将以下内容放入您的设置中:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

如果未指定,默认 timeout 将是 socket.getdefaulttimeout() 提供的,默认为 None(无超时)。


控制台后端

控制台后端不会发送真实的电子邮件,而是编写将发送到标准输出的电子邮件。 默认情况下,控制台后端写入 stdout。 您可以通过在构建连接时提供 stream 关键字参数来使用不同的类流对象。

要指定此后端,请将以下内容放入您的设置中:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

此后端不打算用于生产 - 它是为了方便开发而提供的。


文件后端

文件后端将电子邮件写入文件。 为在此后端打开的每个新会话创建一个新文件。 写入文件的目录来自 :setting:`EMAIL_FILE_PATH` 设置或 file_path 创建与 get_connection() 的连接时的关键字.

要指定此后端,请将以下内容放入您的设置中:

EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location

此后端不打算用于生产 - 它是为了方便开发而提供的。

3.1 版更改:添加了 pathlib.Path 的支持。


内存后端

'locmem' 后端将消息存储在 django.core.mail 模块的特殊属性中。 outbox 属性在发送第一条消息时创建。 它是一个列表,其中包含每个将要发送的消息的 EmailMessage 实例。

要指定此后端,请将以下内容放入您的设置中:

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

此后端不打算在生产中使用——它是作为一种方便而提供的,可以在开发和测试期间使用。

Django 的测试运行程序 自动使用此后端进行测试


虚拟后端

顾名思义,虚拟后端对您的消息没有任何作用。 要指定此后端,请将以下内容放入您的设置中:

EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'

此后端不打算用于生产 - 它是为了方便开发而提供的。


定义自定义电子邮件后端

如果您需要更改电子邮件的发送方式,您可以编写自己的电子邮件后端。 设置文件中的 :setting:`EMAIL_BACKEND` 设置就是后端类的 Python 导入路径。

自定义电子邮件后端应子类化位于 django.core.mail.backends.base 模块中的 BaseEmailBackend。 自定义电子邮件后端必须实现 send_messages(email_messages) 方法。 此方法接收 EmailMessage 实例列表并返回成功传递的邮件数。 如果您的后端有任何持久会话或连接的概念,您还应该实现 open()close() 方法。 有关参考实现,请参阅 smtp.EmailBackend


发送多封电子邮件

建立和关闭 SMTP 连接(或任何其他网络连接,就此而言)是一个昂贵的过程。 如果您要发送大量电子邮件,重用 SMTP 连接是有意义的,而不是每次要发送电子邮件时创建和销毁连接。

有两种方法可以告诉电子邮件后端重用连接。

首先,您可以使用 send_messages() 方法。 send_messages() 获取 EmailMessage 实例(或子类)的列表,并使用单个连接将它们全部发送。

例如,如果您有一个名为 get_notification_email() 的函数,它返回一个 EmailMessage 对象列表,表示您希望发送的某些定期电子邮件,您可以使用对 send_messages 的单个调用发送这些电子邮件:

from django.core import mail
connection = mail.get_connection()   # Use default email connection
messages = get_notification_email()
connection.send_messages(messages)

在这个例子中,调用 send_messages() 在后端打开一个连接,发送消息列表,然后再次关闭连接。

第二种方法是使用电子邮件后端的 open()close() 方法来手动控制连接。 send_messages()如果已经打开连接,不会手动打开或关闭连接,所以如果手动打开连接,可以控制何时关闭。 例如:

from django.core import mail
connection = mail.get_connection()

# Manually open the connection
connection.open()

# Construct an email message that uses the connection
email1 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to1@example.com'],
    connection=connection,
)
email1.send() # Send the email

# Construct two more messages
email2 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to2@example.com'],
)
email3 = mail.EmailMessage(
    'Hello',
    'Body goes here',
    'from@example.com',
    ['to3@example.com'],
)

# Send the two emails in a single call -
connection.send_messages([email2, email3])
# The connection was already open so send_messages() doesn't close it.
# We need to manually close the connection.
connection.close()

为开发配置电子邮件

有时您根本不希望 Django 发送电子邮件。 例如,在开发网站时,您可能不想发送数千封电子邮件——但您可能想要验证电子邮件将在合适的条件下发送给合适的人,并且这些电子邮件将包含正确的内容.

为本地开发配置电子邮件的最简单方法是使用 控制台 电子邮件后端。 此后端将所有电子邮件重定向到标准输出,允许您检查邮件的内容。

file 电子邮件后端在开发过程中也很有用——这个后端将每个 SMTP 连接的内容转储到一个文件中,您可以在闲暇时检查。

另一种方法是使用“哑” SMTP 服务器,该服务器在本地接收电子邮件并将其显示给终端,但实际上并不发送任何内容。 Python 有一种内置的方法可以使用单个命令完成此操作:

python -m smtpd -n -c DebuggingServer localhost:1025

此命令将启动一个最小的 SMTP 服务器,监听 localhost 的 1025 端口。 该服务器将所有电子邮件标题和电子邮件正文打印到标准输出。 然后您只需要相应地设置 :setting:`EMAIL_HOST`:setting:`EMAIL_PORT`。 有关 SMTP 服务器选项的更详细讨论,请参阅 smtpd 模块的 Python 文档。

有关在应用程序中对电子邮件发送进行单元测试的信息,请参阅测试文档的 电子邮件服务 部分。