python标准库中内置了邮件支持模块smtplib,但是包装了smtplib的Flask-Mail能更好的和Flask程序集成。
Flask-Mail连接到SMTP(简单邮件传输协议:simple mail transfer protocol)服务器,把邮件交给这个服务器发送。
# pyCharm Terminal
pip install flask-mail
什么是Shell:
在计算机科学中,Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
# hello.py
import os #操作系统模块
from flask import Flask, render_template, session, redirect, url_for #引入Flask类、render_templdate函数,session类等
from flask_bootstrap import Bootstrap #引入Bootstrap类
from flask_moment import Moment
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_mail import Mail, Message
basedir = os.path.abspath(os.path.dirname(__file__)) #当前文件所在的绝对路径
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string' #Flask-wtf需要的配置
app.config['SQLALCHEMY_DATABASE_URI'] =\ #Flask-SQLAlchemy需要的配置
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #Flask-SQLAlchemy需要的配置
app.config['MAIL_SERVER'] = 'smtp.sina.com' #Flask-Mail需要的配置:邮件服务器地址
app.config['MAIL_PORT'] = 587 #Flask-Mail需要的配置:网络端口
app.config['MAIL_USE_TLS'] = True #Flask-Mail需要的配置:是否开启TLS
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') #Flask-Mail需要的配置:邮箱用户名(值从系统环境获取)
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') #Flask-Mail需要的配置:邮箱密码(值从系统环境获取)
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]' #Flask-Mail需要的配置:邮件主题
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin ' #Flask-Mail需要的配置:发件人
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN') #Flask-Mial需要的配置:收件人地址
bootstrap = Bootstrap(app)
moment = Moment(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
mail = Mail(app)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User', backref='role', lazy='dynamic')
def __repr__(self):
return '' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return '' % self.username
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)
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
@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']:
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))
print('hello')
if __name__=='__main__':
app.run()
来看:
# hello.py
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)
构造好Message的实例msg,再设置body和html属性,最后调用Mail的实例的send()就可以直接发送 。
Mail对应于邮箱:服务器地址、用户名、密码、网络端口、是否使用TLS或SSL等参数,都在mail =Mail(app)构造时设置了。
Message对应于邮件:邮件主题、发送方(name
最后调用mail.send()方法发送。
def send_async_email(app,msg):
with app.app_context(): #调用app.app_context()方法,激活程序上下文
mail.send(msg) #mail.send()需要激活程序上下文current_app(在不同线程中,程序上下文要使用app.current_app()人工创建)
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
mail.send()需要激活程序上下文current_app(在不同线程中,程序上下文要使用app.current_app()人工创建)
这样改完之后,客户端不需要等待,可以直接看到页面。因为,最耗时间的发送邮件步骤由后台的其他线程去执行了。
本想试试:使用多进程实现异步发送邮件
# hello.py
from multiprocessing import Process
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 = Process(target=send_async_email,args=[app,msg])
thr.start() #让另一个线程去发送邮件,程序接着往下走
return thr
出现了EOFError:文件尾错误
# hello.py
import asyncio
@asyncio.coroutine
def use_asyncio_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)
a = yield from mail.send(msg)
return a
@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']:
# send_email(app.config['FLASKY_ADMIN'], 'New User',
# 'mail/new_user', user=user)
loop = asyncio.get_event_loop()
loop.run_until_complete(use_asyncio_send_email(app.config['FLASKY_ADMIN'], 'New User','mail/new_user', user=user))
loop.close()
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))
错误分析:mail.send(msg) 没有返回值(即None),yield from 后面必须要跟一个generator(Iterable 可迭代的)。因此,出现此错误 。