发送邮件

阅读: 25336     评论:6

在Python中内置了smtplib邮件发送模块,Django在此基础上进行了简单地封装,让我们在Django环境中可以更方便更灵活的发送邮件。

所有的功能都在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,
)

导入功能模块,然后发送邮件,so easy!

默认情况下,使用配置文件中的EMAIL_HOSTEMAIL_PORT设置SMTP服务器主机和端口,EMAIL_HOST_USEREMAIL_HOST_PASSWORD是用户名和密码。如果设置了EMAIL_USE_TLSEMAIL_USE_SSL,它们将控制是否使用相应的加密链接。

配置参考:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.sina.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'xxx@sina.com'
EMAIL_HOST_PASSWORD = 'xxxxxxxxxxx'

单发 send_mail()

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

大多数情况下,我们使用send_mail()方法足矣,它接收一系列参数,其中的subjectmessagefrom_emailrecipient_list参数是必须的,其它的可选。

  • subject:邮件主题。字符串。
  • message:邮件具体内容。字符串。
  • from_email:邮件发送者。字符串。
  • recipient_list:收件人。一个由邮箱地址组成的字符串列表。recipient_list中的每一个成员都会在邮件信息的“To:”区域看到其它成员。
  • fail_silently: 一个布尔值。默认值为False,表示send_mail发送失败时,将会引发一个smtplib.SMTPException异常。
  • auth_user: 可选的用户名用来验证SMTP服务器,如果你要特别指定使用哪个邮箱帐号,就指定这个参数。如果没有提供这个值,Django将会使用settings中EMAIL_HOST_USER的值。如果两者都不提供,那你还发什么???
  • auth_password: 可选的密码用来验证SMTP服务器。如果没有提供这个值,Django 将会使用settings中EMAIL_HOST_PASSWORD的值。和上面那个参数是一家的。
  • connection: 可选的用来发送邮件的电子邮件后端。
  • html_message: 如果提供了html_message,可以发送带HTML代码的多媒体邮件。

send_mail()方法的返回值是成功发送的邮件数量(只会是0或1,因为它只能发送一封邮件)。

示例:

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

群发 send_mass_mail()

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

send_mass_mail()用来处理批量邮件的发送任务,也就是所谓的群发。

它的参数中,datatuple是必需参数,接收一个二维元组,二维元组的每个元素也是一个元组,并且格式如下:

(subject, message, from_email, recipient_list)

所有参数的意义与send_mail()中的相同。

例如,以下代码将向两组不同的收件人发送两封不同的邮件,但复用了同一条连接:

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()方法时,每调用一次,它会和SMTP服务器建立一次连接,也就是发一次连一次,效率很低。而send_mass_mail(),则只建立一次连接,就将所有的邮件都发送出去,效率比较高。

给管理员发送邮件

mail_admins()

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

向定义在ADMINS配置项中的网站管理员快速发送邮件。

它会在邮件主题前面添加 EMAIL_SUBJECT_PREFIX配置项指定的前缀,默认是[Django]

邮件的 发件人由 SERVER_EMAIL 配置项指定。

这个方法主要是为了方便快捷和突出邮件主题更醒目。

mail_managers()

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

类似 mail_admins(),不同的是,它是向 MANAGERS 配置项指定的管理员们发送邮件。

防止头部注入攻击

有时候,我们要根据用户表单的输入来构造电子邮件,这就存在头部注入攻击的风险,Django给我们提供了一定的防范能力,但是更多时候,还需要你自己编写安全防范代码。

下面是一个例子,接收用户输入的主题、邮件内容和发送方,将邮件发送到系统管理员:

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.")

如果检查到用户的输入带有头部注入攻击的可能性,会弹出BadHeaderError异常。

EmailMessage 类

Django 的 send_mail()send_mass_mail() 函数其实是对 EmailMessage 类的简单封装利用。

如果你想用进阶功能,比如密送收件人,附件,分段邮件,需要直接创建 EmailMessage 的实例。

EmailMessage 负责构造邮件,邮件后端负责发送邮件。

EmailMessage 类通过以下参数构造邮件(可选参数要按指定顺序提供)。所有的参数都是可选的,且可在调用 send() 方法前设置。

  • subject: 邮件的主题。
  • body: 邮件内容,需要为纯文本格式。
  • from_email: 发件人地址。 fred@example.comFred 形式都是合法的。若省略,则使用 DEFAULT_FROM_EMAIL 配置的值。
  • to: 一个包含收件人地址的列表或元组。
  • bcc: 一个包含地址的列表或元组,指定“密送”对象。
  • connection: 一个邮件后端的实例。若在发送多份邮件时,若想复用连接,则设置此参数。如果省略,在调用 send() 时总会创建新连接。
  • attachments: 附加在邮件中的附件列表。 可以是 MIMEBase 的实例,或 (filename,content,mimetype) 的元组。
  • headers: 一个字典,包含邮件中额外的头信息。字典的关键字是头的名称,值为头的值。需要由调用者确保头名和值的正确性。对应的属性是 extra_headers
  • cc: 一个包含收件人地址的列表或元组,指定“抄送”对象。
  • 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"},
)

EmailMessage 类拥有以下方法:

  • send(fail_silently=False) 发送邮件。若在构建邮件时指定了连接,则会使用这个连接。否则,会实例化并使用一个默认的后端。若指定关键字参数 fail_silentlyTrue,发送邮件时抛出的异常会被和谐掉。一个空的收件人列表不会抛出异常。

  • message() 构建了一个 django.core.mail.SafeMIMEText 对象( MIMEText 的子类)或一个 django.core.mail.SafeMIMEMultipart 对象用于存储邮件内容。如果你继承了 EmailMessage ,你可能需要重写这个方法,在 MIME 对象中放入你期望的内容。

  • recipients() 返回一个包含邮件所有收件人的列表,不管他们是收件人,抄送人,密送人中的哪一个。这可能是另一个你在创建子类时想重写的方法,因为 SMTP 服务器需要你在发送邮件时告诉它完整的收件人列表。如果你在子类中实现了另一个方法,指定收件人列表,这个方法必须也返回相同的结果。

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

  • 可以仅传送一个 MIMEBase 的实例。这会被直接插入邮件。

  • 可以向 attach() 传递 3 个参数: filenamecontentmimetypefilename 是文件附件的名字,它会显示在邮件中, content 是附件包含的数据,而 mimetype 是一个可选参数,指定附件的 MIME 类型。如果你省略了 mimetype,MIME 类型将会参考附件的文件名。

    例如:

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

    对于以 text/ 开头的 mimetype 类型,其内容应该是字符串。二进制数据则将尝试以 UTF-8 解码,如果失败了,MIME 类型会被改为application/octet-stream ,并不会修改数据内容。

  • attach_file() 通过从本地文件系统中选择一个文件的方式创建附件。调用时,传入文件的路径。附件的 MIME 类型是可选的。如果省略了 MIME 类型,会参考文件名。比如:

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

发送多媒体邮件

默认情况下,发送的邮件都是纯文本格式的。但有时候我们希望能在邮件里带一些超级链接、图片,甚至视频和JS动作。

Django为我们提供了一个EmailMultiAlternatives类,可以同时发送文本和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()

邮件后端

发送邮件的动作是由邮件后端执行的。

邮件后端类拥有以下方法:

  • 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,则会从settings.py中相应设置项中读取:

  • host: EMAIL_HOST
  • port: EMAIL_PORT
  • username: EMAIL_HOST_USER
  • password: EMAIL_HOST_PASSWORD
  • use_tls: EMAIL_USE_TLS
  • use_ssl: EMAIL_USE_SSL
  • timeout: EMAIL_TIMEOUT
  • ssl_keyfile: EMAIL_SSL_KEYFILE
  • ssl_certfile: EMAIL_SSL_CERTFILE

基于控制台的后端

控制台后端仅将邮件发送至标准输出,而不是真的发送。默认情况下,控制台后端输出至 stdout。在创建连接时,你可以提供 stream 关键字参数来使用另一个类似 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' # 文件的存储路径

该后端不是为了在生产环境使用的,只是方便你在开发阶段测试使用。

基于内存的后端

该缓存式后端将内容存在 django.core.mail 模块的某个属性值中。

要使用该后端,将以下代码加入你的配置中:

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

该后端不是为了在生产环境使用的,只是方便你在开发阶段测试使用。

Django 的测试器自动为测试使用这个后端。

虚拟后端

就像该后端的名字表示的一样,该后端对你发送的消息什么也不做。

要使用该后端,将以下代码加入你的配置中:

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

该后端不是为了在生产环境使用的,只是方便你在开发阶段测试使用。

使用场景

在开发和测试过程中,大多数情况下你并不想 Django 真的发送邮件。举个例子,在开发网站时,你可能并不期望发送成千上万封邮件——但你想要确保这些邮件会在正确的时间,包含正确的内容,发送给正确的人。

最简单的方式就是使用控制台后端。这个后端将所有的邮件重定向至标准输出,允许你观察邮件的内容。

文件后端在开发时也很有用——这个后端会将SMTP 连接的内容输出至一个文件,你可以在你闲暇时查看这个文件。

另一个方法是使用一个“哑巴” SMTP 服务器,它从本地接收邮件,并输出至终端,并不会真的发送什么。Python 有一个aiosmtpd库可以实现这一功能:

python -m pip install aiosmtpd

python -m aiosmtpd -n -l localhost:8025

该命令会在本机启动一个极小的 SMTP 服务器,监听 8025 端口。这个服务器会在标准输出打印所有的邮件头和邮件内容。

保持发送连接

创建和关闭 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()

 聚合内容 RSS/Atom Django 日志 

评论总数: 6


点击登录后方可评论

EMAIL_HOST_PASSWORD不是密码,而是qq邮箱的授权码



密码只是为了能通俗易懂而已,不需要追究字眼上的问题,还有请你委婉的讲出来好?注重以下他人的成果



os.environ['DJANGO_SETTINGS_MODULE']='mydjsite.setttings'和 django.setup()已配置过,但是还是出现报错:django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.这个怎么处理?



最有可能的是没有正确配置。 先参考http://www.liujiangblog.com/course/django/113 确保能跟着成功做一遍。



我发现是因为import导入了无关的模块而导致错误!按道理如果导入了多余的模块,应该没啥影响。看来若没有删掉或注释掉多余的模块很可能会导致意想不到的错误!



http://www.liujiangblog.com/course/django/163