Flask常用扩展(Extentions)

官网;http://flask.pocoo.org/extensions/
1、Flask-Script

​ 说明: 一个flask终端运行的解析器
安装:pip install flask-script
使用

   # 导入类库
   from flask_script import Manager

   # 创建对象
   manager = Manager(app)

   # 启动实例
   if __name__ == '__main__':
       # app.run(debug=True, threaded=True, port=5050, host='0.0.0.0')
       #使用manager.py来启动服务
       manager.run()

​ 启动

 python manage.py runserver
 python manage.py runserver -d -r --thread

​ 启动参数

   -?,--help # 查看启动设置帮助
   -h,--host # 指定主机
   -p,--port # 指定端口
   --thread # 启动多线程
   -d # 开启调试模式
   -r # 代码修改后自动加载
2、Flask-Bootstrap

​ 安装:pip install flask-bootstrap
使用

from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)
事后配置app:bootstrap.init_app(app) 
{% extends 'bootstrap/base.html' %}

​ 主要block
title | title标签
head | head标签
styles | 引入css
metas | 一组meta标签
body | body标签
navbar | 用户自定义导航条
content | 用户自定义内容
scripts | 用户定义的JS
定义项目基础模板
1.从bootcss.com复制一个顺眼的导航条
2.将container-fluid改为container
3.显示反色导航条:navbar-inverse
4.将圆角改为直角:style=“border-radius: 0px;”
5.根据需要定制显示内容
6.修改折叠目标的定位:data-target=".navbar-collapse"
{% extends ‘bootstrap/base.html’ %}
错误页面定制
定义404错误处理函数

@app.errorhandler(404) 
def page_not_found(e): 
    return render_template('404.html') 

​ 404.html

{% extends 'base.html' %} 
{% block title %}出错了{% endblock %} 
{% block pagecontent %} 
臣妾实在找不到啊@@ 
{% endblock %}

​ 反向路由
url_for('var', name='xiaoming', pwd='123456')
url_for('var', name='xiaoming', pwd='123456', _external=True)
加载静态资源

加载图片:
<img src="{{ url_for('static', filename='cluo.jpg') }}">

加载CSS:
{% block styles %} {{ super() }} 
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='common.css') }}" /> 
{% endblock %}

加载JS:
{% block scripts %} {{ super() }} 
<script type="text/javascript" src="{{ url_for('static', filename='common.js') }}">
</script> 
{% endblock %}

3Flask-Mail

​ 说明:是一个邮件发送的扩展库,使用非常方便
安装:pip install flask-mail
基本配置

配置邮箱服务器:
app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER', 'smtp.qq.com')
配置用户名:
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME', 'xxx@qq.com')
配置密码(或授权码):
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD', '123456')

​ QQ邮箱的额外配置

app.config['MAIL_DEBUG'] = True  # 开启debug,便于调试看信息
app.config['MAIL_SUPPRESS_SEND'] = False  # 发送邮件,为True则不发送
app.config['MAIL_SERVER'] = 'smtp.qq.com'  # 邮箱服务器
app.config['MAIL_PORT'] = 465  # 端口
app.config['MAIL_USE_SSL'] = True  # 重要,qq邮箱需要使用SSL
app.config['MAIL_USE_TLS'] = False  # 不需要使用TLS
app.config['MAIL_DEFAULT_SENDER'] = 'xxx61@qq.com'  # 填邮箱,默认发送者

​ 定义邮件消息

创建邮件消息
msg = Message(subject=subject, recipients=[to], sender=app.config['MAIL_USERNAME'])
浏览器打开邮件的样式
msg.html = render_template(template, **kwargs)
配置邮件正文
msg.body = render_template(template, **kwargs)

​ 发送邮件
创建邮件对象:mail = Mail(app)
事后配置app:mail.init_app(app)
创建发送线程:thr = Thread(target=async_send_mail, args=[app, msg])
执行发送线程:thr.start()
发送邮件:mail.send(message=msg)

注意此处要独占应用上下文: 
# 发送邮件需要程序上下文,新的线程没有上下文,需要手动创建 
with app.app_context(): 
        # 发送邮件 
        mail.send(message=msg)

​ 发送附件

with app.open_resource("image.png") as  fp: 
	msg.attach("image.png", "image/png", fp.read())
4、Flask-WTF

​ a.原生表单
模板文件login.html

   <form method="post" action="{{ url_for('check') }}">
       用户名:<input name="username" type="text" />
       <input type="submit" value="立即登录" />
   </form>

​ 视图函数

#渲染
   @app.route('/login/')
   def login():
       return render_template('login.html')
#校验
   @app.route('/check/', methods=['POST'])
   def check():
       return 'Hello %s !' % request.form['username']

#渲染校验合写
   @app.route('/login/', methods=['GET', 'POST'])
   def login():
       if request.method == 'GET':
           return render_template('login.html')
       else:
           return 'Hello %s !' % request.form['username']

一个路由处理时,表单的action属性不用书写,默认提交到当前路由。
b. 说明:WTF是一个用于表单处理的扩展库,提供了CSRF、校验等功能,使用非常方便.
c.安装:pip install flask-wtf
d.使用
导入依赖
导入表单基类:from flask_wtf import FlaskForm
导入控件/字段:from wtforms import StringField, SubmitField
导入验证规则:from wtforms.validators import DataRequired
e.配置CSRF秘钥
app.config[‘SECRET_KEY’] = ‘123456’
f.定义表单类
class NameForm(FlaskForm)
name = StringField(‘用户名’, validators=[DataRequired()])
submit = SubmitField(‘提交’)

   # 导入表单基类
   from flask_wtf import FlaskForm
   # 导入字段类型
   from wtforms import StringField, SubmitField
   # 导入验证器类
   from wtforms.validators import DataRequired

   # CSRF需要使用
   app.config['SECRET_KEY'] = '123456'

   # 定义表单类
   class NameForm(FlaskForm):
       name = StringField('用户名', validators=[DataRequired()])
       submit = SubmitField('提交')

​ g.视图函数
创建表单对象: form = NameForm()
分配到模板中进行渲染:return render_template(‘form.html’, form=form)

   @app.route('/')
   def index():
       # 创建表单对象
       form = NameForm()
       # 分配到模板中进行渲染
       return render_template('form.html', form=form)

​ h.模板渲染表单
原生渲染

<form> 
{# CSRF的隐藏字段 #} 
{{ form.hidden_tag() }} 

{# name字段,可以指定id、class等属性,定制显示效果 #} 
 {{ form.name.label() }} 
{{ form.name(id='xxx', class='yyy') }} 

{# submit字段 #} 
{{ form.submit() }} 
</form>

​ bootstrap渲染
{% extends ‘bootstrap/base.html’ %}
{% import ‘bootstrap/wtf.html’ as wtf %}
渲染表单

{% block content %}      
{{ wtf.quick_form(form) }} 
{% endblock %}

​ 表单校验

@app.route('/', methods=['GET', 'POST']) 
def index(): 
# 创建表单对象 
form = NameForm() 

# 表单校验 
if form.validate_on_submit(): 
    name = form.name.data 
    form.name.data = '' 


# 分配到模板中进行渲染 
return render_template('form.html', form=form, name=name)

​ 重定向

@app.route('/', methods=['GET', 'POST']) 
def index(): 
# 创建表单对象 
form = NameForm() 

# 表单校验 
if form.validate_on_submit(): 
    session['name'] = form.name.data 
    return redirect(url_for('index')) 

name = session.get('name') 

# 分配到模板中进行渲染 
return render_template('form.html', form=form, name=name)

​ 常见字段类型

StringField | 普通文本字段
SubmitField | 提交按钮
PasswordField | 密码文本字段
HiddenField | 隐藏文本字段
TextAreaField | 多行文本字段
DateField | 文本字段,datetime.date格式
DateTimeField | 文本字段,datetime.datetime格式
IntegerField | 文本字段,值为整数
FloatField | 文本字段,值为小数
BooleanField | 复选框,值为True或False
RadioField | 单选框
SelectField | 下拉框
FileField | 文件上传

​ 常见验证器类

DataRequired | 确保字段有值
Email | 邮箱地址
IPAddress | IP地址
Length | 规定字符长度
EqualTo | 验证两个字段的一致性
URL | 有效的URL
NumberRange | 输入数值的范围
Regexp | 正则验证

​ 自定义字段验证

# 定义表单类 
class NameForm(FlaskForm): 

name = StringField('用户名', validators=[DataRequired()]) submit = SubmitField('提交')
# 自定义字段验证函数,格式是写一个'validate_字段名'的函数
def validate_name(self, field):
     if len(field.data) < 6:
         raise ValidationError('用户名长度不能少于6个字符')
5、flash消息

​ 提交flash消息

@app.route('/', methods=['GET', 'POST']) 
def index(): 

    # 创建表单对象 
    form = NameForm() 

    # 表单校验 
    if form.validate_on_submit(): 
        last_name = session.get('name') 

        # 原来有名字,并且与新的不同 
        if last_name and last_name != form.name.data: 

        # flash消息 
        flash('大哥,又换签名了') 
        flash('常换签名,才能吸引眼球') 
        session['name'] = form.name.data 

        return redirect(url_for('index')) 

name = session.get('name') 
# 分配到模板中进行渲染 
return render_template('form.html', form=form, name=name)

​ 显示消息

 {# 获取所有的flash消息,然后遍历显示 #} 
 {% for message in get_flashed_messages() %} 

    <div class="alert alert-warning alert-dismissible" role="alert"> 
        <button type="button" class="close" data-dismiss="alert" aria-label="Close"> 
            <span aria-hidden="true">&times;</span> 
        </button> 
        {{message}} 
    </div> 

{% endfor %}

​ 提示
若好多页面都有弹出消息,可以将flash消息放在基础模板中展示

6、Flask-Moment

​ 说明
专门负责时间本地化显示的扩展库,使用非常方便
安装
pip install flask-moment
使用
向模板传递时间

# 导入类库 
from flask_moment import Moment 
from datetime import datetime, timedelta 

# 创建对象 
moment = Moment(app) 

@app.route('/moment/') 

def mom(): 
    current_time = datetime.utcnow() + timedelta(seconds=-3600) 
    return render_template('moment.html', current_time=current_time)

​ 模板中显示

   {# 简单的格式化显示 #} 
   <div>时间:{{ moment(current_time).format('LLLL') }}</div> 
   <div>时间:{{ moment(current_time).format('LLL') }}</div> 
   <div>时间:{{ moment(current_time).format('LL') }}</div> 
   <div>时间:{{ moment(current_time).format('L') }}</div> 

   {# 自定义格式化显示 #} 
   <div>自定义显示:{{ moment(current_time).format('YYYY-MM-DD') }}</div> 

   {# 时间差值显示 #} 
   <div>发表于:{{ moment(current_time).fromNow() }}</div> 

   {# 加载jQuery,因为moment.js依赖,使用bootstrap时可以省略 #} 
   {{ moment.include_jquery() }} 

   {# 加载moment.js #} 
   {{ moment.include_moment() }} 

   {# 设置中文显示 #} 
   {{ moment.locale('zh-CN') }}

​ 提示
格式化显示不要记忆,去官网查询即可
momentjs.com

6、Flask-Uploads

​ a.原生上传文件
b.模板文件

   <html>
   <head>
       <title>原生文件上传</title>
   </head>
   <body>
       {% if img_url %}
           <img src="{{ img_url }}">
       {% endif %}
       <form method="post" enctype="multipart/form-data">
           <input type="file" name="photo" />
           <input type="submit" value="上传" />
       </form>
   </body>
   </html>

​ c.视图函数

   @app.route('/upload/', methods=['GET', 'POST'])
   def upload():
       img_url = None
       if request.method == 'POST':
           file = request.files.get('photo')
           if file and allowed_file(file.filename):
               # 获取文件后缀
               suffix = os.path.splitext(file.filename)[1]
               # 生成随机文件名
               filename = random_string() + suffix
               # 保存上传文件
               file.save(os.path.join(app.config['UPLOAD_FOLDER'],
                                      filename))
               # 构造上传的文件的访问URL
               img_url = url_for('uploaded', filename=filename)
       return render_template('upload.html', img_url=img_url)

​ 相关配置

   # 允许上传的文件后缀
   ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
   # 配置上传文件保存位置
   app.config['UPLOAD_FOLDER'] = os.getcwd()
   # 上传文件大小
   app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 8

​ 支持函数
判断是否是允许的文件后缀

   def allowed_file(filename):
       return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

​ 生成随机的字符串

   def random_string(length=32):
       import random
       base_str = 'abcdefghijklmnopqrstuvwxyz1234567890'
       return ''.join(random.choice(base_str) for i in range(length))

​ 获取上传文件

   @app.route('/uploaded/<filename>')
   def uploaded(filename):
       # 安全的发送文件
       return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

​ 注意事项,文件上传失败时,应从哪些方面着手
1.表单的提交方法必须是POST
2.表单的enctype属性必须设置为multipart/form-data
3.上传的字段类型必须为file,并且必须有name属性
4.是否超过运行的最大尺寸
5.文件的保存位置是否有空间,是否有权限
生成缩略图
需要安装pillow库

   # 导入图片处理的类,默认PIL不支持python3.x,安装pillow后就可以了
   from PIL import Image

   # 生成缩略图
   # 1.打开文件
   img = Image.open(pathname)
   # 2.重设尺寸
   img.thumbnail((128, 128))
   # 3.保存修改
   img.save(pathname)

​ flask-uploads
说明:在文件上传时,提供了很大的方便,如:文件类型的过滤,校验,文件保存,获取url等
安装:pip install flask-uploads
使用
相关配置

   # 上传文件的大小
   app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 8
   # 上传文件的保存位置
   app.config['UPLOADED_PHOTOS_DEST'] = os.getcwd()

   # 创建上传对象,指定过滤的文件后缀
   photos = UploadSet('photos', IMAGES)
   configure_uploads(app, photos)
   # 配置上传文件大小,默认64M,
   #设置为None时使用MAX_CONTENT_LENGTH选项
   patch_request_class(app, size=None)

​ 视图函数

   @app.route('/upload/', methods=['GET', 'POST'])
   def upload():
       img_url = None
       if request.method == 'POST' and 'photo' in request.files:
           # 保存文件
           filename = photos.save(request.files['photo'])
           # 获取保存文件的url
           img_url = photos.url(filename)
       return render_template('upload.html', img_url=img_url)

​ 模板文件
同原生的文件上传upload.html
d.结合wtf做文件上传
flask-uploads配置同上
flask-wtf配置

   # 导入表单基类
   from flask_wtf import FlaskForm
   # 导入文件上传字段及验证器
   from flask_wtf.file import FileField, FileRequired, FileAllowed
   from wtforms import SubmitField

   # 上传文件表单类
   class UploadForm(FlaskForm):
       photo = FileField('头像上传', validators=[FileRequired('文件未选择'),
      FileAllowed(photosUS, message='只能上传图片文件')])
       submit = SubmitField('上传')

​ 视图函数

   @app.route('/upload/', methods=['GET', 'POST'])
   def upload():
       img_url = None
       form = UploadForm()
       if form.validate_on_submit():
           # 获取文件后缀
           suffix = os.path.splitext(form.photo.data.filename)[1]
           # 生成所及文件名
           filename = random_string() + suffix
           # 保存上传文件
           photos.save(form.photo.data, name=filename)
  
           # 生成缩略图
           pathname = os.path.join(app.config['UPLOADED_PHOTOS_DEST'],
            filename)
           # 打开文件
           img = Image.open(pathname)
           # 设置尺寸
           img.thumbnail((128, 128))
           # 保存文件
           img.save(pathname)
           # 获取上传文件的url
           img_url = photos.url(filename)
       return render_template('upload.html', form=form, img_url=img_url)

​ 模板文件upload.html

   {% extends 'bootstrap/base.html' %}

   {% import 'bootstrap/wtf.html' as wtf %}

   {% block content %}
       <div class="container">
           {% if img_url %}
               <img src="{{ img_url }}" />
           {% endif %}
           {{ wtf.quick_form(form) }}
       </div>
   {% endblock %}

​ 常用API

配置默认的上传文件大小:
app.config['MAX_CONTENT_LENGTH'] = 8 * 1024 *1024
配置图片格式的上传文件存储路径:
app.config['UPLOADED_PHOTOS_DEST'] = os.getcwd()

定义一个图片格式的上传对象:
photos = UploadSet('photos', IMAGES)
保存上传对象到指定路径
filename = uploadSet.save(request.files['photo'])
filename = uploadSet.save(form.filefield.data, name=form.filefield.data.filename)
获得指定文件的访问url:
file_url = uploadSet.url(filename)

绑定app和uploadSet对象:configure_uploads(app, photos)
指定上传文件的大小:patch_request_class(app, size=6410241024)
allowed_file(file.filename)
suffix = os.path.splitext(file.filename)[1]       
csrf校验:
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY','123456')
Bootstrap对象:
bootstrap = Bootstrap(app)
创建表单:
class UploadForm(FlaskForm):
文件对象:
photo = FileField('photo',validators=[FileRequired('必须选择文件'),FileAllowed(photos,'必须是图片类型')])
提交对象:
submit = SubmitField('UPLOAD')
表单校验:
if form.validate_on_submit():
	pass
继承bootstrap:
{% extends 'bootstrap/base.html' %}
引入wtf:
{% import 'bootstrap/wtf.html' as wtf %}
快速集成表单:
{{ wtf.quick_form(form) }}

7、Flask-Login

​ 初始化和配置
登录管理器对象:login_manager = LoginManager(app)
动态绑定app:login_manager.init_app(app)
登录重定向路由:login_manager.login_view = ‘user.login’
定义登录的路由函数
登录重定向信息:login_manager.login_message = ‘需要登录才可访问’
登录提示信息
设置session的保护级别:login_manager.session_protection = ‘strong’
basic=默认级别
strong=加强保护
None=禁用
配置用户模型
class User(UserMixin, db.Model):
登录认证的回调

@login_manager.user_loader 
def load_user(uid): 
       return User.query.get(int(uid)) 

​ 返回一个用户对象(以便存取用户状态)
使用
登录(记住我)
login_user(u, remember=form.remember.data)
获取当前登录用户
current_user
当前登录用户
是否已登录:current_user.is_authenticated
是否匿名:current_user.is_anonymous
u = current_user._get_current_object()
登出
logout_user()
路由保护

@app.route('/test/') 
@login_required 
   def test(): 
       return '登录后才可访问的页面'