在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_HOST
和EMAIL_PORT
设置SMTP服务器主机和端口,EMAIL_HOST_USER
和EMAIL_HOST_PASSWORD
是用户名和密码。如果设置了EMAIL_USE_TLS
和EMAIL_USE_SSL
,它们将控制是否使用相应的加密链接。
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)
让我们来了解一下send_mail()
方法,它接收一系列参数,其中的subject
、message
、from_email
和recipient_list
参数是必须的,其它的可选。
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
的值。和上面那个参数是一家的。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(datatuple, 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()
,则只建立一次链接,就将所有的邮件都发送出去,效率比较高。
示例:
datatuple = ( ('Subject', 'Message.', 'from@example.com', ['john@example.com']), ('Subject', 'Message.', 'from@example.com', ['jane@example.com']), ) send_mass_mail(datatuple)
有时候,我们要根据用户表单的输入来构造电子邮件,这就存在头部注入攻击的风险,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异常。
Django 的 send_mail()
和 send_mass_mail()
函数其实是对 EmailMessage
类的简单封装利用。
如果你想用进阶功能,比如密送收件人,附件,分段邮件,你需要直接创建 EmailMessage
的实例。
EmailMessage
负责构造邮件,邮件后端负责发送邮件。
EmailMessage
类通过以下参数构造邮件(可选参数要按指定顺序提供)。所有的参数都是可选的,且可在调用 send()
方法前设置。
subject
: 邮件的主题。body
: 邮件内容,需要为纯文本格式。from_email
: 发件人地址。 fred@example.com
和 Fred
形式都是合法的。若省略,则使用 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_silently
为 True
,发送邮件时抛出的异常会被和谐掉。一个空的收件人列表不会抛出异常。
message()
构建了一个 django.core.mail.SafeMIMEText
对象( MIMEText
的子类)或一个 django.core.mail.SafeMIMEMultipart
对象用于存储邮件内容。如果你继承了 EmailMessage
,你可能需要重写这个方法,在 MIME 对象中放入你期望的内容。
recipients()
返回一个包含邮件所有收件人的列表,不管他们是收件人,抄送人,密送人中的哪一个。这可能是另一个你在创建子类时想重写的方法,因为 SMTP 服务器需要你在发送邮件时告诉它完整的收件人列表。如果你在子类中实现了另一个方法,指定收件人列表,这个方法必须也返回相同的结果。
attach()
创建一个新的附件,并加到邮件。有两种调用 attach()
的方式:
可以仅传送一个 MIMEBase
的实例。这会被直接插入邮件。
可以向 attach()
传递 3 个参数: filename
, content
和 mimetype
。 filename
是文件附件的名字,它会显示在邮件中, 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()
默认情况下,EmailMessage
的 body
参数的 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 后端(默认值)外,其它后端应仅在开发和测试阶段使用。如果对发送邮件有特殊的需求,你可以编写自定义后端](https://docs.djangoproject.com/zh-hans/3.1/topics/email/#topic-custom-email-backend)。
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
模块的某个属性值中。outbox
属性会在第一条消息发送时创建。这是一个列表,每项都是一个 EmailMessage
实例,代表一条要被发送的消息。
要使用该后端,将以下代码加入你的配置中:
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
该后端不是为了在生产环境使用的,只是方便你在开发阶段测试使用。
Django 的测试器自动为测试使用这个后端。
就像该后端的名字表示的一样,该后端对你发送的消息什么也不做。
要使用该后端,将以下代码加入你的配置中:
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
该后端不是为了在生产环境使用的,只是方便你在开发阶段测试使用。
在开发和测试过程中,大多数情况下你并不想 Django 真的发送邮件。举个例子,在开发网站时,你可能并不期望发送成千上万封邮件——但你想要确保这些邮件将会在正确的时间,包含正确的内容,发送给正确的人。
最简单的方式就是使用控制台后端。这个后端将所有的邮件重定向至标准输出,允许你观察邮件的内容。
文件后端在开发时也很有用——这个后端将每次 SMTP 连接的内容输出至一个文件,你可以在你闲暇时查看这个文件。
另一个方法是使用一个“哑巴” SMTP 服务器,它从本地接收邮件,并输出至终端,并不会真的发送什么。Python 有一个内置的方式可以实现,仅需一行代码:
python -m smtpd -n -c DebuggingServer localhost:1025
该命令会在本机启动一个极小的 SMTP 服务器,监听 1025 端口。这个服务器在标准输出打印所有的邮件头和邮件内容。接下来,你只需要配置 EMAIL_HOST
和 EMAIL_PORT
。更多关于 SMTP 服务器配置的细节讨论,参考 smtpd
模块的Python 文档。
创建和关闭 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()
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