使用Flask-Mail提供电子邮件支持
表6-1 Flask-Mail SMTP服务器的配置
配置 |
默认值 |
说明 |
MAIL_SERVER |
localhost |
电子邮件服务器的主机名或IP地址 |
MAIL_PORT |
25 |
电子邮件服务器的端口 |
MAIL_USE_TLS |
False |
启用传输层安全(TLS)协议 |
MAIL_USE_SSL |
False |
启用安全套接层(SSL)协议 |
MAIL_USENAME |
None |
邮件账户的用户名 |
MAIL_PASSWORD |
None |
邮件账户的授权码 |
- 在
hello.py
中配置Flask-Mail使用QQ邮箱:
1 2 3 4 5 6 7 8
| 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')
|
注意:千万不要把账号密令直接写在脚本中,应该从环境变量中导入。
- 初始化Flask-Mail:
1 2 3 4
| from flask_mail import Mail # ... mail = Mail(app)
|
- 在环境变量中定义
MAIL_USERNAME
和MAIL_PASSWORD
:
1 2
| (venv) $ export MAIL_USERNAME='12345678@qq.com' (venv) $ export MAIL_PASSWORD='qwertyuiop'
|
此时可以使用echo
命令打印出来检查一下:
1 2 3 4
| (venv) $ echo $MAIL_USERNAME >>> '12345678@qq.com' (venv) $ echo $MAIL_PASSWORD >>> 'qwertyuiop'
|
定义环境变量后,此时在同一个终端中执行python hello.py shell
命令,便能获取到想要的环境变量。
注意:如果定义好环境变量后把终端关闭再重新打开,那么此时是没有上次定义的环境变量的。所以需要在同一个终端中执行。
如何让QQ邮箱开启SMTP功能,可以参考flask-mail常见的邮箱配置问题解决
在Python shell中发送电子邮件
在上一个终端中,发送电子邮件:
1 2 3 4 5 6 7 8
| (venv) $ python hello.py shell >>> from flask_mail import Message >>> from hello import mail >>> msg = Message('test subject', sender='12345678@qq.com', recipients=['87654321@qq.com']) >>> msg.body = 'test body' >>> msg.html = '<p>test body</p>' >>> with app.app_context(): ··· mail.send(msg)
|
注意:在Flask-Mail中的send()
函数使用current_app
(程序上下文),因此在shell中发送邮件,需要激活程序上下文。
在程序中集成发送电子邮件功能
- 定义用于发邮件的函数,使
hello.py
支持电子邮件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| from flask_mail import Message # ... # 给邮件标题添加一个前缀 app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]' # 定义发件人 app.config['FLASKY_MAIL_SENDER'] = '12345678@qq.com' def send_email(recipients, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject, sender=app.config['FLASKY_MAIL_SENDER'], recipients=[recilients]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) mail.send(msg)
|
邮件有纯文本.txt
,也有HTML文本.html
,客户端显示哪个,取决于邮件客户端的设置。
拓展:为什么send()
函数中需要**kwargs
?**kwargs
的作用是什么?
答:(1)因为我们不确定模板中需要什么变量参数,在此例中,模板需要的时user
这个参数,但是如果换成别的模版,它不仅需要user
参数,也需要其他一些参数(如datatime等,关键取决于模板设计成什么样),此时如果我们死死地把send_email()
函数写成send_email(recipient, subject, template, user)
,那么就失去了灵活性,当换成其他模版时,datatime
参数也就无法传入了,因此send()
函数需要**kwargs
。
(2)**kwargs
的作用是:当我们不知道需要往函数中传入多少个关键字参数或者想以字典的形式作为参数时,我们可以用**kwargs
,这样我们就可以根据实际情况需要,往函数中传入特定个数的参数(数量使具体情况而定)。
- 结合视图函数发送电子邮件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| # ... # 定义收件人为Flasky的管理员 app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN') # ... @app.route('/', methods=['GET', 'POST'] def index(): form = NameForm() if form.validate_on_submit(): session['name'] = form.name.data # 查找数据库中是否有该用户名,如果没有,就向数据库中添加新用户, # 因为前面已经设置了SQLALCHEMY_COMMIT_ON_TEARDOWN,所以当请求结束时会自动提交事务 user = User.query.filter_by(user_name=form.name.data).first() if user is None: # 插入数据库 user = User(user_name=form.name.data) db.session.add(user) # 用于模板中判断是显示Pleased to meet you 还是 Happy to see you again session['known'] = Flase # 如果收件人不为空,则发送邮件 if app.config['FLASKY_ADMIN']: send_email(app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user) else: session['konwn'] = True return redirect(url_for('index') return render_template('index.html', form=form, name=session.get('name'), known=session.get('known', Flase))
|
此时我们也需要定义环境变量FLASKY_ADMIN
:
(venv) $ export FLASKY_ADMIN='12345678@qq.com'
template
文件夹下的模板文件mail/new_user
有两个,分别为new_user.txt
和new_user.html
。
new_user.txt
如下:
1
| There has a new user that name is {{ user.user_name }}
|
new_user.html
如下:
1
| <h1>There has a new user that name is {{ user.user_name }}</h1>
|
异步发送电子邮件
为了避免处理请求过程中不必要的延迟,我们可以把发送电子邮件的函数移到后台线程中处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from threading import Thread # ... def send_async_email(app, msg): with app.app_context(): mail.send(msg) def send_email(recipients, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject, sender=app.config['FLASKY_MAIL_SENDER'], recipients=[recilients]) 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
|
注意:由于在不同线程中执行mail.send()
函数,就如前面在Python shell中发送电子邮件章节中提到,send()
函数需要程序current_app
(程序上下文)中执行,因此需要在执行send()
函数的线程中使用app.app_context()
人工创建current_app
。
当需要发送大量电子邮件时,使用专门发送电子邮件的作业(如Celery任务队列)处理更合适。