Flask學習(5)——電子郵件

在 Python 標準庫中通常使用 smtplib 包發送電子郵件,而 Flask 中的 Flask-Mail 擴展不僅包裝了 smtplib,且能更好的與 Flask 集成。首先在虛擬環境中安裝此擴展:

pip install flask-mail

Flask-Mail文檔:http://www.pythondoc.com/flask-mail/index.html

一、配置

Flask-Mail 連接到簡單郵件傳輸協議(SMTP,simple mail transferprotocol)服務器,把郵件交給這個服務器發送。如果不進行配置,則 Flask-Mail 連接 localhost 上的 25 端口,無須驗證身份即可發送電子郵件。

Flask-Mail SMTP 服務器配置:

配置 默認值 說明
MAIL_SERVER localhost 電子郵件服務器的主機名或 IP 地址
MAIL_PORT 25 電子郵件服務器的端口
MAIL_USE_TLS False 啓用傳輸層安全(TLS,transport layer security)協議
MAIL_USE_SSL False 啓用安全套接層(SSL,secure sockets layer)協議
MAIL_USERNAME None 郵件賬戶的用戶名
MAIL_PASSWORD None 郵件賬戶的密碼

實際中,連接到外部 SMTP 服務器更方便,如下例使用 qq郵箱的配置:

import os
# ...
app.config['MAIL_SERVER'] = 'smtp.qq.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')

注意qq郵箱需要先開啓SMTP服務,並得到授權碼:
在這裏插入圖片描述
MAIL_USERNAME爲郵箱號,MAIL_PASSWORD 的值即爲生成的授權碼。

由於QQ郵箱不支持非加密的協議,那麼使用加密協議,分爲兩種加密協議,選擇其中之一即可

  • MAIL_USE_TLS:端口號是587
  • MAIL_USE_SSL:端口號是465

Flask-Mail在使用前也需要進行初始化:

from flask_mail import Mail
mail = Mail(app)

保存電子郵件服務器用戶名和密碼的兩個環境變量要在環境中定義。如果你使用的是 Linux 或 macOS,可以按照下面的方式設定這兩個變量:

export MAIL_USERNAME=<mail username>
export MAIL_PASSWORD=<mail password>

微軟 Windows 用戶可按照下面的方式設定環境變量:

# cmd終端
set MAIL_USERNAME=<mail username>
set MAIL_PASSWORD=<mail password>

# powershell終端
$env:MAIL_USERNAME='<mail username>'
$env:MAIL_PASSWORD='<mail password>'

二、在Python shell中發送電子郵件

打開一個 shell 會話(powershell),來發送一個測試郵件。

先配置一下環境變量,上面的方式定義的是臨時環境變量,每個新shell都需要導入一次。

$env:FLASK_APP='.\hello.py'
$env:MAIL_USERNAME='[email protected]'
$env:MAIL_PASSWORD='你的授權碼'

然後打開 flask shell 進行測試:

>>> from flask_mail import Message
>>> from hello import mail
>>> msg = Message('test email', sender='[email protected]',  recipients=['[email protected]'])
>>> msg.body = 'This is the plain text body'
>>> msg.html = 'This is the <b>HTML</b> body'
>>> with app.app_context():
...     mail.send(msg)

注意:Flask-Mail 的 send() 函數使用 current_app ,因此要在激活的應用上下文中執行。

成功收到郵件:
在這裏插入圖片描述


三、在應用中集成郵件發送功能

一般把發送電子郵件的部分定義爲一個函數,這樣還可以使用 Jinja2 模板渲染郵件正文,靈活性高。

from flask_mail import Message

# 主題的前綴
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
# 發件人地址
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <[email protected]>'

def sned_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_mail() 函數的參數分別爲收件人地址(to)、主題(subject)、渲染郵件正文的模板(template)、關鍵字參數列表(**kwargs)。
  • 指定模板時不包含擴展名,這樣才能使用兩個模板分別渲染txt和HTML。
  • 調用者傳入
    的關鍵字參數將傳給 render_template() 函數,作爲模板變量提供給模板使用,用於生成電子郵件正文。

下面我們修改視圖函數 index(),使表單每接收到新的名字,應用就給管理員發送一封電子郵件,修改hello.py如下:

# ...
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)
            db.session.commit()
            session['known'] = False
            # 發送電子郵件
            if app.config['FLASKY_ADMIN']:
                sned_email(app.config['FLASKY_ADMIN'], 'New User',
                          'mail/new_user', user=user)
        else:
            session['known'] = True
        session['name'] = form.name.data
        session['message'] = user.message
        form.name.data = ''
        return redirect(url_for('index'))
    return render_template('index.html',
                           form=form, name=session.get('name'),
                           known=session.get('known', False),
                           message=session.get('message'))

templates/mai/new_user.txt:

User {{ user.username }} has joined.

templates/mai/new_user.html:

User <b>{{ user.username }}</b> has joined.
  • 電子郵件的收件人地址保存在環境變量 FLASKY_ADMIN 中,啓動前需要導入此環境變量,方法和前面的相同。
  • 我們還需要創建兩個模板文件,分別用於渲染純文本和HTML版本的郵件正文。這兩個模板文件都保存在 templates 目錄下的 mail 子目錄中。
  • 電子郵件的模板中有一個模板參數是用戶,因此調用 send_email() 函數時要以關鍵字參數的形式傳入用戶。

現在每次你在表單中填寫新名字(如email test),管理員(FLASKY_ADMIN)都會收到一封電子郵件。

在這裏插入圖片描述


四、異步發送電子郵件

在上面的例子中,我們發現在發送電子郵件的時候,網頁會停滯一會,爲了避免用戶感覺到這樣的延遲,可以把發送電子郵件的函數移到後臺線程中,修改方法如下:

from threading import Thread

# ...
def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)


def sned_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() 人工創建應用上下文。app 實例作爲參數傳入線程,因此可以通過它來創建上下文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章