发送电子邮件

尽管 Python 通过 smtplib 模块提供邮件发送接口,但 Django 提供了几个轻量级包装器。提供这些包装器是为了让发送电子邮件变得非常快速,帮助在开发期间测试电子邮件发送,并为无法使用 SMTP 的平台提供支持。

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

快速示例

两行代码

from django.core.mail import send_mail

send_mail(
    "Subject here",
    "Here is the message.",
    "[email protected]",
    ["[email protected]"],
    fail_silently=False,
)

邮件使用 EMAIL_HOSTEMAIL_PORT 设置中指定的 SMTP 主机和端口发送。如果设置了 EMAIL_HOST_USEREMAIL_HOST_PASSWORD 设置,则使用它们对 SMTP 服务器进行身份验证,而 EMAIL_USE_TLSEMAIL_USE_SSL 设置控制是否使用安全连接。

注意

使用 django.core.mail 发送的电子邮件的字符集将设置为 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 将使用 DEFAULT_FROM_EMAIL 设置的值。
  • 收件人列表:一个字符串列表,每个字符串都是一个电子邮件地址。 收件人列表 的每个成员都将在电子邮件消息的“收件人:”字段中看到其他收件人。
  • 静默失败:一个布尔值。当它为 False 时,如果发生错误,send_mail() 将引发 smtplib.SMTPException。有关可能发生的异常列表,请参阅 smtplib 文档,它们都是 SMTPException 的子类。
  • auth_user:用于向 SMTP 服务器进行身份验证的可选用户名。如果未提供此项,Django 将使用 EMAIL_HOST_USER 设置的值。
  • auth_password:用于向 SMTP 服务器进行身份验证的可选密码。如果未提供此项,Django 将使用 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_password 的功能与 send_mail() 中相同。

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

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

message1 = (
    "Subject here",
    "Here is the message",
    "[email protected]",
    ["[email protected]", "[email protected]"],
)
message2 = (
    "Another Subject",
    "Here is another message",
    "[email protected]",
    ["[email protected]"],
)
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() 是发送电子邮件给站点管理员的快捷方式,如 ADMINS 设置中定义的那样。

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

电子邮件的“发件人:”标头将是 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() 相同,只是它会向网站管理员发送电子邮件,如 MANAGERS 设置中所定义。

示例

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

send_mail(
    "Subject",
    "Message.",
    "[email protected]",
    ["[email protected]", "[email protected]"],
)

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

datatuple = (
    ("Subject", "Message.", "[email protected]", ["[email protected]"]),
    ("Subject", "Message.", "[email protected]", ["[email protected]"]),
)
send_mass_mail(datatuple)

防止头部注入

头部注入 是一种安全漏洞,攻击者在其中插入额外的电子邮件头部来控制脚本生成的电子邮件消息中的“收件人:”和“发件人:”。

上面概述的所有 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, ["[email protected]"])
        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:发件人的地址。[email protected]"Fred" <[email protected]> 两种形式都是合法的。如果省略,则使用 DEFAULT_FROM_EMAIL 设置。
  • to:收件人地址的列表或元组。
  • bcc:发送电子邮件时用于“Bcc”标头中的地址列表或元组。
  • connection:电子邮件后端实例。如果您想为多封电子邮件使用相同的连接,请使用此参数。如果省略,则在调用 send() 时会创建一个新连接。
  • attachments:要放入邮件中的附件列表。这些可以是 MIMEBase 实例,或 (filename, content, mimetype) 三元组。
  • headers:要放入邮件中的额外标头的字典。键是标头名称,值是标头值。由调用者确保标头名称和值采用电子邮件消息的正确格式。相应的属性是 extra_headers
  • cc:发送电子邮件时用于“Cc”标头中的收件人地址列表或元组。
  • reply_to:发送电子邮件时用于“回复到”标头中的收件人地址列表或元组。

例如

from django.core.mail import EmailMessage

email = EmailMessage(
    "Hello",
    "Body goes here",
    "[email protected]",
    ["[email protected]", "[email protected]"],
    ["[email protected]"],
    reply_to=["[email protected]"],
    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")
      

      如果你指定 mimetypemessage/rfc822,它还将接受 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", "[email protected]", "[email protected]"
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() 将返回 EMAIL_BACKEND 中指定的电子邮件后端的实例。如果您指定 backend 参数,将实例化该后端的实例。

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

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

Django 附带了多个电子邮件发送后端。除了 SMTP 后端(这是默认后端)之外,这些后端仅在测试和开发期间有用。如果您有特殊的电子邮件发送要求,您可以 编写自己的电子邮件后端

SMTP 后端

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

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

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

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

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

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

控制台后端

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

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

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

此后端不适用于生产环境,而是作为一种便利工具,可在开发期间使用。

文件后端

文件后端将电子邮件写入文件。在该后端上打开的每个新会话都会创建一个新文件。写入文件的目录要么取自 EMAIL_FILE_PATH 设置,要么取自使用 get_connection() 创建连接时的 file_path 关键字。

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

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

此后端不适用于生产环境,而是作为一种便利工具,可在开发期间使用。

内存后端

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

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

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

此后端不适用于生产环境 - 提供它作为开发和测试期间可使用的便利功能。

Django 的测试运行器 自动将此后端用于测试

虚拟后端

顾名思义,虚拟后端对您的消息不执行任何操作。要指定此后端,请在您的设置中放置以下内容

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

此后端不适用于生产环境,而是作为一种便利工具,可在开发期间使用。

定义自定义电子邮件后端

如果您需要更改电子邮件的发送方式,您可以编写自己的电子邮件后端。设置文件中的 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",
    "[email protected]",
    ["[email protected]"],
    connection=connection,
)
email1.send()  # Send the email

# Construct two more messages
email2 = mail.EmailMessage(
    "Hello",
    "Body goes here",
    "[email protected]",
    ["[email protected]"],
)
email3 = mail.EmailMessage(
    "Hello",
    "Body goes here",
    "[email protected]",
    ["[email protected]"],
)

# 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 发送电子邮件。例如,在开发网站时,您可能不想发送数千封电子邮件 - 但您可能希望验证电子邮件是否会在正确的情况下发送给正确的人,并且这些电子邮件将包含正确的内容。

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

文件电子邮件后端在开发期间也很有用 - 此后端会将每次 SMTP 连接的内容转储到一个可供您随时检查的文件中。

另一种方法是使用“哑”SMTP 服务器,它在本地接收电子邮件并将其显示到终端,但实际上不会发送任何内容。aiosmtpd 包提供了一种实现此目的的方法

python -m pip install aiosmtpd

python -m aiosmtpd -n -l localhost:8025

此命令将启动一个在 localhost 的端口 8025 上侦听的最小 SMTP 服务器。此服务器会将所有电子邮件头和电子邮件正文打印到标准输出。然后,您只需相应地设置EMAIL_HOSTEMAIL_PORT。有关 SMTP 服务器选项的更详细讨论,请参阅aiosmtpd 模块的文档。

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

返回顶部