flask成长记(五)

flask成长记(五)

很多类型的应用程序都需要在特定事件发生时提醒用户,而常用的通信方法是电子邮件。
虽然 Python 标准库中的 smtplib 包可用在 Flask 程序中发送电子邮件,但包装了 smtplib 的
Flask-Mail 扩展能更好地和 Flask 集成。

其实之前用的代码也很好,直接就能发邮件。

这里其实说更好,其实一点都不好。

使用Flask-Mail提供电子邮件支持

同样的也是需要外部的包提供支持。

使用 pip 安装 Flask-Mail:

pip install flask-mail

Flask-Mail 连接到简单邮件传输协议( Simple Mail Transfer Protocol, SMTP)服务器,并
把邮件交给这个服务器发送。 如果不进行配置, Flask-Mail 会连接 localhost 上的端口 25,
无需验证即可发送电子邮件。

Flask-Mail SMTP服务器的配置:
MAIL_SERVER localhost 电子邮件服务器的主机名或 IP 地址
MAIL_PORT 25 电子邮件服务器的端口
MAIL_USE_TLS False 启用传输层安全( Transport Layer Security, TLS)协议
MAIL_USE_SSL False 启用安全套接层( Secure Sockets Layer, SSL)协议
MAIL_USERNAME None 邮件账户的用户名
MAIL_PASSWORD None 邮件账户的密码

在开发过程中,如果连接到外部 SMTP 服务器,则可能更方便。举个例子,示例 6-1 展示
了如何配置程序,以便使用 Google Gmail 账户发送电子邮件。

其实大部分都是连接到外部服务器啊,不过有的服务器不安全登录可能不成功,比如qq邮箱就不能成功。
之前蜗牛用163的邮箱,是可以登录并发送邮件的。

配置 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')

千万不要把账户密令直接写入脚本,特别是当你计划开源自己的作品时。为
了保护账户信息,你需要让脚本从环境中导入敏感信息。

初始化

from flask.ext.mail import Mail
mail = Mail(app)

flask的框架中有很多这样的做法,xxx=Xxx(app)
其中app是那个对象。

微软 Windows 用户可按照下面的方式设定环境变量:

(venv) $ set MAIL_USERNAME=<Gmail username>
(venv) $ set MAIL_PASSWORD=<Gmail password>

这里面的venv是虚拟环境。也算一个沙箱的作用。

在程序中集成发送电子邮件功能

为了避免每次都手动编写电子邮件消息,我们最好把程序发送电子邮件的通用部分抽象出
来,定义成一个函数。 这么做还有个好处,即该函数可以使用 Jinja2 模板渲染邮件正文,
灵活性极高。

其实都是定义成一个函数了,之前蜗牛做的都是定义成一个函数的。

from flask_mail import Mail, Message


@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
        return redirect(url_for('index'))
    return render_template('index.html', form=form, name=session.get('name'),
                           known=session.get('known', False))

是不是很不好看!

下面这个模板好看多了:

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)

这里用到了模板。他说是利用jinja2的模板渲染正文,上面那个栗子用的模板就是之前用的那个。

这个函数用到了两个程序特定配置项,分别定义邮件主题的前缀和发件人的地址。 send_
email 函数的参数分别为收件人地址、主题、渲染邮件正文的模板和关键字参数列表。指定
模板时不能包含扩展名,这样才能使用两个模板分别渲染纯文本正文和富文本正文。调用者
将关键字参数传给 render_template() 函数,以便在模板中使用,进而生成电子邮件正文。
index() 视图函数很容易被扩展,这样每当表单接收新名字时,程序都会给管理员发送一
封电子邮件。

除了前面提到的环境变量 MAIL_USERNAME 和 MAIL_PASSWORD 之外,这个版本的程序还需要使
用环境变量 FLASKY_ADMIN。

对微软 Windows 用户来说:

(venv) $ set FLASKY_ADMIN=<Gmail username>

异步发送电子邮件

如果你发送了几封测试邮件,可能会注意到 mail.send() 函数在发送电子邮件时停滞了几
秒钟,在这个过程中浏览器就像无响应一样。为了避免处理请求过程中不必要的延迟,我
们可以把发送电子邮件的函数移到后台线程中。修改方法如下:

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

对于python的多线程机制还得再看看。

上述实现涉及一个有趣的问题。很多 Flask 扩展都假设已经存在激活的程序上下文和请求
上下文。 Flask-Mail 中的 send() 函数使用 current_app,因此必须激活程序上下文。不过,
在不同线程中执行 mail.send() 函数时,程序上下文要使用 app.app_context() 人工创建。

现在再运行程序,你会发现程序流畅多了。不过要记住,程序要发送大量电子邮件时,使
用专门发送电子邮件的作业要比给每封邮件都新建一个线程更合适。 例如,我们可以把执
行 send_async_email() 函数的操作发给 Celery( http://www.celeryproject.org/)任务队列。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值