许多类型的应用程序都会在某些事件发生的时候通知用户,经常使用的沟通方法就是电子邮件。尽管在Flask应用程序中,可使用Python标准库中的smtplib
包来发送电子邮件,不过Flask-Mail扩展封装了smtplib
且与Flask整合的很是好。html
使用pip安装Flask-Mail:python
(venv) $ pip install flask-mail
扩展链接到一个简单邮件传输协议(SMTP)服务器并将邮件传递给它由它递送。若是没有给出配置,Flask-Mail则链接到localhost25端口并发送无验证的电子邮件。表6-1所示的配置键列表能够用来配置SMTP服务器。git
表格6-1. Flask-Mail SMTP服务器配置键github
在开发过程当中若是能链接到一个外部SMTP服务器会更方便。示例6-1展现了如何配置应用程序经过谷歌的Gmail账户发送电子邮件。web
示例6-1. hello.py:Flask-Mail配置Gmailshell
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')
建议:永远不要将帐户证书直接写在你的脚本里面,尤为是若是你打算将你的的工做开源。为了保护你的账户信息,必须让脚本从你的配置环境中导入敏感信息。flask
示例6-2展现了Flask-Mail的初始化。浏览器
示例6-2. hello.py:Flask-Mail初始化bash
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>
为了测试配置,你能够开启一个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
,因此它须要执行已激活的应用程序上下文。
为了不每次都手动建立电子邮件消息,将应用电子邮件发送功能的共同部分抽象到一个函数中是很是不错的作法。另外一个好处是,这个函数能够用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>
使用这些环境变量设置,当你每次在表单中输入一个新的名字的时候均可以测试应用程序和接收电子邮件。
若是你发送一些测试邮件,你可能注意到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
脚本开始大,这使得它变得更难管理。在下一章中,你将学习如何构建一个更大的应用程序。