许多类型的应用程序都会在某些事件发生的时候通知用户,常用的沟通方法就是电子邮件。尽管在Flask应用程序中,可以使用Python标准库中的smtplib
包来发送电子邮件,不过Flask-Mail扩展封装了smtplib
且与Flask整合的非常好。
1、使用Flask-Mail作为邮件支持
使用pip安装Flask-Mail:
(venv) $ pip install flask-mail
扩展连接到一个简单邮件传输协议(SMTP)服务器并将邮件传递给它由它递送。如果没有给出配置,Flask-Mail则连接到localhost25端口并发送无验证的电子邮件。表6-1所示的配置键列表可以用来配置SMTP服务器。
表格6-1. Flask-Mail SMTP服务器配置键
在开发过程中如果能连接到一个外部SMTP服务器会更方便。示例6-1展示了如何配置应用程序通过谷歌的Gmail帐户发送电子邮件。
示例6-1. hello.py:Flask-Mail配置Gmail
import os
# ...
app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
建议:永远不要将账户证书直接写在你的脚本里面,尤其是如果你打算将你的的工作开源。为了保护你的帐户信息,必须让脚本从你的配置环境中导入敏感信息。
示例6-2展示了Flask-Mail的初始化。
示例6-2. hello.py:Flask-Mail初始化
from flask.ext.mail import Mail
mail = Mail(app)
持有email服务器用户名和密码的两个变量需要在环境中定义。如果你是使用Linux或Mac OS X上的bash,你可以设置这些变量如下:
(venv) $ export MAIL_USERNAME=<Gmail username>
(venv) $ export MAIL_PASSWORD=<Gmail password>
对于Windows用户,可以设置环境变量如下:
(venv) $ set MAIL_USERNAME=<Gmail username>
(venv) $ set MAIL_PASSWORD=<Gmail password>
1.1、使用python shell发送邮件
为了测试配置,你可以开启一个shell会话并发送测试email:
(venv) $ python hello.py shell
>>> from flask.ext.mail import Message
>>> from hello import mail
>>> msg = Message('test subject', sender='you@example.com',
... recipients=['you@example.com'])
>>> msg.body = 'text body'
>>> msg.html = '<b>HTML</b> body'
>>> with app.app_context():
... mail.send(msg)
...
注意,Flask-Mail的send()
函数使用current_app
,所以它需要执行已激活的应用程序上下文。
1.2、集成邮件到应用程序
为了避免每次都手动创建电子邮件消息,将应用电子邮件发送功能的共同部分抽象到一个函数中是非常不错的做法。另一个好处是,这个函数可以用Jinja2模板尽情渲染。示例6-3展示了怎么实现。
示例6-3. hello.py:Email支持
from flask.ext.mail import Message
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <flasky@example.com>'
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
mail.send(msg)
为发件人的主题和地址定义前缀字符串的函数依赖于两个特定于应用程序的配置键。send_email
函数携带目的地址、主题、邮件体模板和一组关键字参数。模板名不能有扩展,这样两个版本的模板可以使用纯文本或富文本。调用者传递关键字参数给render_template()
调用,这样模板就可以生成email体。
index()
视图函数可以非常容易的扩充用来发送一个email给管理员,当从表单中收到一个新的名字的时候。示例6-4展示所做的改动。
示例6-4. hello.py:Email示例
# ...
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN')
# ...
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
session['known'] = False
if app.config['FLASKY_ADMIN']:
send_email(app.config['FLASKY_ADMIN'], 'New User',
'mail/new_user', user=user)
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html', form=form,
name=session.get('name'),
known=session.get('known', False))
启动过程中,在FLASKY_ADMIN
环境变量中给出的email收件人会加载到同名的配置变量中。需要创建文本和HTML两个版本的email模板文件。这些文件都存储在template
内的mail
子目录中,让他们独立于普通模板。电子邮件模板需要给出用户作为该模板参数,因此send_email()
调用包括用户来作为一个关键字参数。
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行
git checkout 6a
来切换到这个版本的应用程序。
除了前面描述的MAIL_USERNAME
和MAIL_PASSWORD
环境变量,这个版本的应用程序需要FLASKY_ADMIN
环境变量。对于Linux和Mac OS X用户来说,启动应用程序命令:
(venv) $ export FLASKY_ADMIN=<your-email-address>
对于Windows用户,命令如下:
(venv) $ set FLASKY_ADMIN=<Gmail username>
使用这些环境变量设置,当你每次在表单中输入一个新的名字的时候都可以测试应用程序和接收电子邮件。
1.3、发送异步邮件
如果你发送一些测试邮件,你可能注意到mail.send()
函数在发送电子邮件的时候会阻塞几秒钟,这段时间浏览器看起来没有响应。为了避免请求处理不必要的延误,可以将邮件发送功能移到一个后台线程去处理。示例6-5展示了以上改动。
示例6-5. hello.py:异步邮件支持
from threading import Thread
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
return thr
这个实现突显了一个有趣的问题。许多Flask扩展操作是在假设有活动的应用程序和请求上下文的情况下进行的。Flask-Mail的send()
函数使用current_app
,所以它需要已激活的应用程序上下文。但是当mail.send()
函数在一个不同的线程上执行,应用程序上下文需要人为地创建使用app.app_context()
。
建议:如果你有克隆在GitHub上的应用程序,你现在可以运行
git checkout 6b
来切换到这个版本的应用程序。
如果你现在运行应用程序,你会发现它响应更快了,但请记住,发送大量的电子邮件的应用程序,其拥有一个致力于发送电子邮件的服务比开启一个新的线程更合适。例如,执行send_async_email()
函数可以将邮件发送到Celery的任务队列中。
本章完成的是大多数web应用程序必备的功能。现在的问题是,hello.py
脚本开始大,这使得它变得更难管理。在下一章中,你将学习如何构建一个更大的应用程序。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。