Skip to main content

正在发送电子邮件

虽然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,
)

使用 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:字符串。

  • recipient_list:字符串列表,每个都是电子邮件地址。 recipient_list 的每个成员都将在电子邮件的“收件人:”字段中看到其他收件人。

  • fail_silently:布尔值。如果是 Falsesend_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 中的收件人将看到电子邮件“To:”字段中的其他地址。

例如,以下代码将向两个不同的收件人集合发送两个不同的消息;然而,只有一个到邮件服务器的连接将被打开:

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() vs. 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] "

电子邮件的“From:”标题将是 SERVER_EMAIL 设置的值。

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

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

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.',
    '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 send_mail, BadHeaderError
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() 和相关的包装函数可用。如果您希望使用高级功能(例如BCC的收件人,文件附件或多部分电子邮件),则需要直接创建 EmailMessage 实例。

注解

这是一个设计特性。 send_mail() 和相关功能最初是Django提供的唯一接口。然而,他们接受的参数列表随时间缓慢增长。为电子邮件移动到更面向对象的设计是有意义的,并保留原始功能仅用于向后兼容性。

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

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

EmailMessage 对象

class EmailMessage[源代码]

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

  • subject:电子邮件的主题行。

  • body:正文文本。这应该是纯文本消息。

  • from_email:发件人的地址。 fred@example.comFred <fred@example.com> 表格都是合法的。如果省略,则使用 DEFAULT_FROM_EMAIL 设置。

  • to:收件人地址的列表或元组。

  • bcc:发送电子邮件时在“Bcc”标题中使用的地址的列表或元组。

  • connection:电子邮件后端实例。如果要对多个消息使用相同的连接,请使用此参数。如果省略,则会在调用 send() 时创建新连接。

  • attachments:要放在邮件上的附件列表。这些可以是 email.MIMEBase.MIMEBase 实例,也可以是 (filename, content, mimetype) 三元组。

  • headers:放在消息上的额外标题的字典。键是标题名称,值是标题值。它取决于调用者确保电子邮件消息的头名称和值的格式正确。相应的属性是 extra_headers

  • cc:发送电子邮件时在“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,则发送消息时产生的异常将被撤消。空列表的收件人不会引发异常。

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

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

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

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

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

      例如:

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

      如果您指定 mimetypemessage/rfc822,它也将接受 django.core.mail.EmailMessageemail.message.Message

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

  • attach_file() 使用文件系统中的文件创建一个新的附件。使用要附加的文件的路径和可选的用于附件的MIME类型调用它。如果省略MIME类型,它将从文件名中猜出。最简单的用法是:

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

发送替代内容类型

在电子邮件中包含多个版本的内容可能很有用;经典的例子是发送消息的文本和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() 的调用将返回在 EMAIL_BACKEND 中指定的电子邮件后端的实例。如果指定 backend 参数,那么该后端的实例将被实例化。

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

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

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

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'

这个后端不是用于生产 - 它是作为一个方便,可以在开发过程中使用。

文件后端

文件后端将电子邮件写入文件。为在此后端上打开的每个新会话创建一个新文件。在创建与 get_connection() 的连接时,写入文件的目录取自 EMAIL_FILE_PATH 设置或从 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'

这个后端不是用于生产 - 它是作为一个方便,可以在开发和测试期间使用。

虚拟后端

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

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',
    '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发送电子邮件。例如,在开发网站时,您可能不想发送成千上万的电子邮件,但您可能需要验证电子邮件将在适当的条件下发送给正确的人员,并且这些电子邮件将包含正确的内容。

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

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

另一种方法是使用“哑”SMTP服务器,它在本地接收电子邮件并将其显示到终端,但实际上不发送任何内容。 Python有一个内置的方法来实现这一点用一个单一的命令:

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

此命令将启动一个简单的SMTP服务器侦听本地主机的端口1025。此服务器只打印标准输出所有电子邮件标头和电子邮件正文。然后只需要相应地设置 EMAIL_HOSTEMAIL_PORT。有关SMTP服务器选项的更详细的讨论,请参阅 smtpd 模块的Python文档。

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