許多類型的應用程序都會在某些事件發生的時候通知用戶,經常使用的溝通方法就是電子郵件。儘管在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
腳本開始大,這使得它變得更難管理。在下一章中,你將學習如何構建一個更大的應用程序。