在Python的Flask框架下收发电子邮件的教程

 简述

在大多数此类教程中都会不遗余力的介绍如何使用数据库。今天我们对数据库暂且不表,而是来关注另一个在web应用中很重要的特性:如何推送邮件给用户。

在某个轻量级应用中我们可能会添加一个如下的邮件服务功能:当用户有了新的粉丝后,我们发送一封邮件通知用户。有很多方法可以实现这个特性,而我们希望提供出一种可复用的通用框架来处理。
 
Flask-Mail介绍

对于我们来说是幸运的,现在已经有很多外部插件来处理邮件,虽说不能百分百按照我们的想法去处理,但已经相当接近了。

在虚拟环境中安装 Flask-Mail是相当简单的。Windows以外的用户可以利用以下命令来安装:

flask/bin/pip install flask-mail

Windows用户的安装稍有不同,因为Flask-Mail所使用的一些模块不能再Windows系统上运行,你可以使用以下命令:

flask\Scripts\pip install --no-deps lamson chardet flask-mail

配置:

回想一下前文中单元测试部分的案例,我们通过添加配置支持了一个这样的功能:当应用的某个版本测试出错时可以邮件通知我们。从这个例子就可以看出如何配置使用邮件支持。

再次提醒大家,我们需要设置两个方面的内容:

  • 邮件服务器信息
  • 用户邮箱地址

如下正是前文中所用到的配置

# email server
MAIL_SERVER = 'your.mailserver.com'
MAIL_PORT = 25
MAIL_USE_TLS = False
MAIL_USE_SSL = False
MAIL_USERNAME = 'you'
MAIL_PASSWORD = 'your-password'

# administrator list
ADMINS = ['you@example.com']

其中并没有设置切实可用的邮件服务器和邮箱。现在我们通过一个例子来看如何使用gmail邮箱账户来发送邮件:

# email server
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 465
MAIL_USE_TLS = False
MAIL_USE_SSL = True
MAIL_USERNAME = 'your-gmail-username'
MAIL_PASSWORD = 'your-gmail-password'

# administrator list
ADMINS = ['your-gmail-username@gmail.com']

另外我们也可以初始化一个Mail对象来连接SMTP邮件服务器,发送邮件:

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

发个邮件试试!

为了了解flask-mail如何工作的,我们可以从命令行发一封邮件看看。进入python shell并执行如下的脚本:

>>> from flask.ext.mail import Message
>>> from app import mail
>>> from config import ADMINS
>>> msg = Message('test subject', sender = ADMINS[0], recipients = ADMINS)
>>> msg.body = 'text body'
>>> msg.html = '<b>HTML</b> body'
>>> mail.send(msg)

上面这段代码会根据inconfig.py中配置的邮箱地址列表,以首个邮箱作为发件人给所有邮箱发送一封邮件。邮件内容会以文本和html两种格式呈现,而你能看到哪种格式取决于你的邮件客户端。

多么简单小巧!你完全可以现在就把它集成到你的应用中。

邮件框架

我们现在可以编写一个帮助函数来发送邮件。这是以上测试中一个通用版的测试。我们把这个函数放进一个新的原文件中用作邮件支持(fileapp/emails.py):

from flask.ext.mail import Message
from app import mail

def send_email(subject, sender, recipients, text_body, html_body):
 msg = Message(subject, sender, recipients)
 msg.body = text_body
 msg.html = html_body
 mail.send(msg)

Flask-Mail的邮件支持超出了我们目前的使用范围,像密件抄送和附件的功能并不会在此应用中得以使用。

Follower 提醒

现在,我们已经有了发邮件的基本框架,我们可以写发送follower提醒的函数了 (fileapp/emails.py):

from flask import render_template
from config import ADMINS

def follower_notification(followed, follower):
 send_email("[microblog] %s is now following you!" % follower.nickname,
  ADMINS[0],
  [followed.email],
  render_template("follower_email.txt",
   user = followed, follower = follower),
  render_template("follower_email.html",
   user = followed, follower = follower))

你在这里找到任何惊喜了吗?我们的老朋友render_template函数有一次出现了。

如果你还记得,我们使用这个函数在views渲染模版. 就像在views里写html不好一样,使用邮件模版是理想的选择。我们要可能的将逻辑和表现分开,所以email模版也会和其它试图模版一起放到在模版文件夹里.

所以,我们需要为follower提醒邮件写纯文本和网页版的邮件模版,下面这个是纯文本的版本 (fileapp/templates/follower_email.txt):

Dear {{user.nickname}},

{{follower.nickname}} is now a follower. Click on the following link to visit {{follower.nickname}}'s profile page:

{{url_for("user", nickname = follower.nickname, _external = True)}}

Regards,

The microblog admin

下面这个是网页版的邮件,效果会更好(fileapp/templates/follower_email.html):

<p>Dear {{user.nickname}},</p>
<p><a href="{{url_for("user", nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a> is now a follower.</p>
<table>
 <tr valign="top">
  <td><img src="{{follower.avatar(50)}}"></td>
  <td>
   <a href="{{url_for('user', nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a><br />
   {{follower.about_me}}
  </td>
 </tr>
</table>
<p>Regards,</p>
<p>The <code>microblog</code> admin</p>

注解:模版中的url_for函数的 _external = True 参数的意义.默认情况下,url_for 函数生成url是相对我们的域名的。例如,url_for("index")函数返回值是/index, 但是,发邮件是我们想要http://localhost:5000/index这种url,email中是没有域上下文的,所以,我们必须强制生成带域名的url,_external argument就是干这个工作的。

最后一步是处理“follow”过程,即触发邮件提醒时的视图函数,(fileapp/views.py):

from emails import follower_notification

@app.route('/follow/<nickname>')
@login_required
def follow(nickname):
 user = User.query.filter_by(nickname = nickname).first()
 # ...
 follower_notification(user, g.user)
 return redirect(url_for('user', nickname = nickname))

现在你可以创建两个用户(如果还没有用户的话)尝试着用让一个用户follow另一个用户,理解邮件提醒是怎样工作的。

就是这样吗?我们做完了吗?

我们可能心底里很兴奋完成了这项工作并且把邮件提醒功能同未完成列表里删除。

但是,如果你现在测试下应用,你会发现当你单击follow链接的时候,页面会2到3秒才会响应,浏览器才会刷新,这在之前是没有的。 
 
发生了什么?

问题是,Flask-Mail 使用同步模式发送电子邮件。 从电子邮件发送开始,直到电子邮件交付后,给浏览器发回其响应,在整个过程中,Web服务器会一直阻塞。如果我们试图发送电子邮件到一个服务器是缓慢的,甚至更糟糕的,暂时处于脱机状态,你能想象会发生什么吗?很不好。

这是一个可怕的限制,发送电子邮件应该是后台任务且不会干扰Web服务器,让我们看看我们如何能够解决这个问题。

Python中执行异步调用

我们想send_email 函数发完邮件后立即返回,需要让发邮件移动到后台进程来异步执行。

事实上python已经对异步任务提供了支持,但实际上,还可以用其他的方式,比如线程和多进程模块也可以实现异步任务。

每当我们需要发邮件的时候,启动一个线程来处理,比启动一个全新的进程节省资源。所以,让我们将mail.send(msg)调用放到另一个线程中。(fileapp/emails.py):

from threading import Thread

def send_async_email(msg):
 mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
 msg = Message(subject, sender = sender, recipients = recipients)
 msg.body = text_body
 msg.html = html_body
 thr = threading.Thread(target = send_async_email, args = [msg])
 thr.start()

如果你测试‘follow‘函数,现在你会发现浏览器在发送邮件之前会刷新。

所以,我们已经实现了异步发送,但是,如果未来在别的需要异步功能的地方难道我们还需要在实现一遍吗?

过程都是一样的,这样就会在每一种情况下都有重复代码,这样非常不好。

我们可以通过 decorator改进代码。使用装饰器的代码是这样的:

from decorators import async

@async
def send_async_email(msg):
 mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
 msg = Message(subject, sender = sender, recipients = recipients)
 msg.body = text_body
 msg.html = html_body
 send_async_email(msg)

更好了,对不对?

实现这种方式的代码实际上很简单,创建一个新源文件(fileapp/decorators.py):

from threading import Thread

def async(f):
 def wrapper(*args, **kwargs):
  thr = Thread(target = f, args = args, kwargs = kwargs)
  thr.start()
 return wrapper

现在我们对异步任务创建了个有用的框架(framework), 我们可以说已经完成了!

仅仅作为一个练习,让我们思考一下为什么这个方法会看上去使用了进程而不是线程。我们并不想每当我们需要发送一封邮件时就有一个进程被启动,所以我们能够使用thePoolclass而不用themultiprocessingmodule。这个类会创建指定数量的进程(这些都是主进程的子进程),并且这些子进程会通过theapply_asyncmethod送到进程池,等待接受任务去工作。这可能对于一个繁忙的网站会是一个有趣的途径,但是我们目前仍将维持现在线程的方式。
 
最后的话
这个 updatedmicroblogapplication 的源代码如下:

下载 microblog-0.11.zip

(0)

相关推荐

  • python通过smpt发送邮件的方法

    本文实例讲述了python通过smpt发送邮件的方法.分享给大家供大家参考.具体实现方法如下: import smtplib, socket fromaddr = a@b.com toaddrs = ["c@d.com", "e@f.com"] msg = open("multimsg.eml", "r").read() try: server = smtplib.SMTP('10.0.0.1') result = serve

  • python通过邮件服务器端口发送邮件的方法

    本文实例讲述了python通过邮件服务器端口发送邮件的方法.分享给大家供大家参考.具体实现方法如下: fromAddress = 'sender@example.com' toAddress = 'me@my.domain' msg = "Subject: Hello\n\nThis is the body of the message." import smtplib server = smtplib.SMTP("localhost", 25) server.se

  • python监控网站运行异常并发送邮件的方法

    本文实例讲述了python监控网站运行异常并发送邮件的方法.分享给大家供大家参考.具体如下: 这是一个简单的python开发的监控程序,当指定网页状态不正常是通过smtp发送通知邮件 复制代码 代码如下: #!/usr/bin/env python # -*- coding: UTF-8 -*- #author  libertyspy import socket import smtplib import urllib mail_options = {     'server':'smtp.qq

  • python登录pop3邮件服务器接收邮件的方法

    本文实例讲述了python登录pop3邮件服务器接收邮件的方法.分享给大家供大家参考.具体实现方法如下: import poplib, string PopServerName = "mail.yourserver.com" PopServer = poplib.POP3(PopServerName) print PopServer.getwelcome() PopServer.user('yourName') PopServer.pass_('yourPass') r, items,

  • python使用mailbox打印电子邮件的方法

    本文实例讲述了python使用mailbox打印电子邮件的方法.分享给大家供大家参考.具体如下: 该范例在linux下使用 import mailbox mailboxname = "/tmp/mymailbox" mbox = mailbox.UnixMailbox(open(mailboxname)) msgcounter = 0 while 1: mailmsg = mbox.next() if not mailmsg: break msgcounter = msgcounter

  • python同时给两个收件人发送邮件的方法

    本文实例讲述了python同时给两个收件人发送邮件的方法.分享给大家供大家参考.具体分析如下: 该范例通过python内置的smtplib包发送邮件 import smtplib import string host = "localhost" fromclause = "a@b.com" toclause = "c@d.com, e@f.com" toclause = string.splitfields(toclause, ",&q

  • 在Python的Flask框架下收发电子邮件的教程

     简述 在大多数此类教程中都会不遗余力的介绍如何使用数据库.今天我们对数据库暂且不表,而是来关注另一个在web应用中很重要的特性:如何推送邮件给用户. 在某个轻量级应用中我们可能会添加一个如下的邮件服务功能:当用户有了新的粉丝后,我们发送一封邮件通知用户.有很多方法可以实现这个特性,而我们希望提供出一种可复用的通用框架来处理.   Flask-Mail介绍 对于我们来说是幸运的,现在已经有很多外部插件来处理邮件,虽说不能百分百按照我们的想法去处理,但已经相当接近了. 在虚拟环境中安装 Flask

  • 在Python的Flask框架下使用sqlalchemy库的简单教程

    flask中的sqlalchemy 相比于sqlalchemy封装的更加彻底一些 , 在一些方法上更简单 首先import类库: 在CODE上查看代码片派生到我的代码片 <span style="font-size:18px;">from flask import Flask from flask.ext.sqlalchemy import SQLAlchemy</span> 然后,需要加载 数据库路径 在CODE上查看代码片派生到我的代码片 <span

  • 在Python的Flask框架中实现单元测试的教程

     概要 在前面的章节里我们专注于在我们的小应用程序上一步步的添加功能上.到现在为止我们有了一个带有数据库的应用程序,可以注册用户,记录用户登陆退出日志以及查看修改配置文件. 在本节中,我们不为应用程序添加任何新功能,相反,我们要寻找一种方法来增加我们已写代码的稳定性,我们还将创建一个测试框架来帮助我们防止将来程序中出现的失败和回滚. 让我们来找bug 在上一章的结尾谈到,我故意在应用程序中引入一个bug.接下来让我描述一下它是什么样的bug,然后看看当我们的程序不按照我们意愿执行的时候,它在其中

  • 在Python的setuptools框架下生成egg的教程

    本文介绍了 setuptools 框架的内容,它是 PEAK 的一个副项目,它提供了比 distutils 更加简单的包管理和发行功能. 开始 setuptools 模块很会 "规避".例如,如果我们下载一个使用 setuptools 而不是使用 distutils 构建的包,那么安装就应该可以像我们期望的一样工作:通常使用 python setup.py install 就可以.为了实现这种功能,使用 setuptools 绑定在一起的包就会在归档文件中包含一个很小的引导模块 ez_

  • 在Python的Django框架下使用django-tagging的教程

    django使用app机制来实现组件的重用,充分的利用已有的app可以极大的简化开发工作.目前django下的app虽然还不够丰富,却也还是有部分不错的.django-tagging就是一个不错的app. 现在tag的应用非常广泛,tag基本上成了各网站的必备项目之一,django-tagging就是一个提供tag功能的app.django-tagging提供的功能非常丰富,使用起来却十分简单.下面我就介绍一些常用的用法,让大家对该app有个基本的了解,更详细的介绍还是老老实实的去看django

  • Python使用Flask框架获取当前查询参数的方法

    本文实例讲述了Python使用Flask框架获取当前查询参数的方法.分享给大家供大家参考.具体如下: 这段代码实现Python的Flask框架下获取当前查询参数,即QueryString中的所有参数 from flask import Flask, render_template, request # Initialize the Flask application app = Flask(__name__) # This is a catch all route, to catch any r

  • Python基于Flask框架配置依赖包信息的项目迁移部署

    一般在本机上完成基于Flask框架的代码编写后,如果有接口或者数据操作方面需求需要把代码部署到指定服务器上. 一般情况下,使用Flask框架开发者大多数都是选择Python虚拟环境来运行项目,不同的虚拟环境中配置依赖包信息不同.如果重新迁移到一个新的虚拟环境后,又重新来一个一个的配置依赖包,那将会很浪费时间. 下面介绍一个简单易用的技巧,也是我自己在书本上看到的,以防每次配置需要翻阅书籍的麻烦,所以单自写一篇文章作记录,方便自己以后查看,也希望给其他学习的同学有点帮助. 完成项目相关代码编写后,

  • Python使用flask框架操作sqlite3的两种方式

    本文实例讲述了Python使用flask框架操作sqlite3的两种方式.分享给大家供大家参考,具体如下: 方式一:raw_sql import sqlite3 from flask import Flask, request, jsonify app = Flask(__name__) DATABASE_URI = ":memory:" # 创建表格.插入数据 @app.before_first_request def create_db(): # 连接 conn = sqlite3

  • Python中flask框架跨域问题的解决方法

    目录 一.跨域是什么 二.如何解决跨域问题 总结 一.跨域是什么 从一个域名去请求另一个域名,这个过程称之为跨域.浏览器从一个域名的网页去请求另一个域名的资源,域名.端口.协议有一个不一样,请求都属于跨域.跨域其实是浏览器的一个保护政策. 网页上有ajax请求时,会报:No 'Access-Control-Allow-Origin' header is present on the requested '这个错误. 二.如何解决跨域问题 1.跨域请求的过程 因此我们只要做到请求头部信息一致即可.

  • python编程Flask框架简单使用教程

    目录 基础知识 使用框架的优点: Flask常用扩展包: 基本格式: 拓展: return 重定向 取网址参数 content-type cookie\session flask路由 request属性 上下文 注册路由 基础知识 使用框架的优点: 稳定性和可扩展性强 可以降低开发难度,提高了开发效率 Flask诞生于2010年,是Armin ronacher用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架 Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件

随机推荐