在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="/static/imghw/default1.png" data-src="{{follower.avatar(50)}}" class="lazy" alt="在Python的Flask框架下收发电子邮件的教程" ></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 。

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。
