Flask 是一个微型 Web 框架,依赖于 jinjia2 模板系统和 Werkzeug WSGI(本质为 Socket 服务端) 服务,默认状况不支持数据库抽象层、表单验证,若是要使用能够进行拓展。css
Flask 经常使用扩展包:html
安装:前端
pip3 install flask
Werkzeug:python
#!/usr/bin/env python # -*- coding:utf-8 -*- from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello)
尝鲜:shell
from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "Index Page" if __name__ == "__main__": app.run()
官网地址:数据库
Flask 经常使用五种路由:flask
# 前四种能够接收变量 @app.route('/user/<username>') # /user/rose @app.route('/post/<int:post_id>') # /post/1 @app.route('/post/<float:post_id>') # /post/1.2 @app.route('/post/<path:subpath>') # /post/static/css/xx.css @app.route('/login', methods=['GET', 'POST'])
示例:bootstrap
@app.route('/user/<username>') def show_user_profile(username): return 'User %s' % username
以上五种路由基于如下对应关系:segmentfault
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
让 Flask 支持正则:https://segmentfault.com/q/1010000000125259
。api
url_for()
方法能够根据视图函数名,反解出 url,相似于 Django 的 reverse()
方法:
# 接收视图函数名字,返回所对应的绝对 URL url_for('index', _external=True)
from flask import Flask, redirect, url_for app = Flask(__name__) @app.route("/") def index(): return "Index Page" @app.route("/hello") def hello(): return "Hello World!" @app.route('/user/<username>') def profile(username): return username with app.test_request_context(): print(url_for('hello')) print(url_for('profile', username='rose')) print(url_for('index')) if __name__ == "__main__": app.run()
test_request_context()
告诉 Flask
,即便咱们使用Python shell
,它也会像处理请求同样行事。
运行结果以下:
/hello /user/rose /
from flask import Flask from werkzeug.routing import BaseConverter class Regex_url(BaseConverter): def __init__(self,url_map,*args): super(Regex_url,self).__init__(url_map) self.regex = args[0] app = Flask(__name__) app.url_map.converters['re'] = Regex_url @app.route('/user/<re("[a-z]{3}"):id>') def hello_itcast(id): return 'hello %s' % id if __name__ == '__main__': app.run()
from flask import Flask, redirect, url_for, render_template app = Flask(__name__) # 静态文件和模板依赖这句 @app.route('/hello/') def hello(): return render_template('t1.html') if __name__ == '__main__': app.run()
静态文件语法:
url_for('static', filename='style.css')
静态文件目录 static
在项目根目录,与程序同级(须要本身建立),Flask 默认回会去 static
中查找,你能够在里面新建子文件夹:
<img src="{{ url_for('static', filename='imgs/1.png') }}">
模板路径设置
Flask 会在 templates
文件夹里寻找模板。因此,若是你的应用是个模块,这个文件夹应该与模块同级;若是它是一个包,那么这个文件夹做为包的子目录:
状况 1: 模块:
/application.py /templates /hello.html
状况 2: 包:
/application /__init__.py /templates /hello.html
Flask 使用的是 Jinja2模板,与 Django 同样,所以语法也没什么差异。
自定义模板
在模板中使用 Python 函数:
app.py
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, render_template app = Flask(__name__) def func(): return '<h1>Flask 自定义模板</h1>' @app.route('/index', methods=['GET', 'POST']) def index(): return render_template('index.html', func=func) if __name__ == '__main__': app.run()
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ func() | safe }} <!-- safe 不转义 --> </body> </html>
语法:
变量名 | 过滤器
字符串过滤器:
# 禁用转义 safe <p>{{ '<em>hello</em>' | safe }}</p> # capitalize:把变量值的首字母转成大写,其他字母转小写; <p>{{ 'hello' | capitalize }}</p> # lower:把值转成小写; <p>{{ 'HELLO' | lower }}</p> # upper:把值转成大写; <p>{{ 'hello' | upper }}</p> # title:把值中的每一个单词的首字母都转成大写; <p>{{ 'hello' | title }}</p> #trim:把值的首尾空格去掉; <p>{{ ' hello world ' | trim }}</p> # reverse:字符串反转; <p>{{ 'olleh' | reverse }}</p> # format:格式化输出; <p>{{ '%s is %d' | format('name',17) }}</p> # striptags:渲染以前把值中全部的HTML标签都删掉; <p>{{ '<em>hello</em>' | striptags }}</p>
列表操做:
# first:取第一个元素 <p>{{ [1,2,3,4,5,6] | first }}</p> # last:取最后一个元素 <p>{{ [1,2,3,4,5,6] | last }}</p> # length:获取列表长度 <p>{{ [1,2,3,4,5,6] | length }}</p> # sum:列表求和 <p>{{ [1,2,3,4,5,6] | sum }}</p> # sort:列表排序 <p>{{ [6,2,3,1,5,4] | sort }}</p>
自定义过滤器
过滤器的本质是函数,两种实现方式:一种是经过Flask应用对象的add_template_filter
方法。还能够经过装饰器来实现自定义过滤器,与内置过滤器重名则覆盖:
一、add_template_filter
,第一个参数是函数名,第二个参数是自定义的过滤器名称
def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort,'double_2')
二、装饰器来实现自定义过滤器,参数为过滤器名字:
@app.template_filter('db3') def filter_double_sort(ls): return ls[::-3]
app.py
from flask import Flask, render_template app = Flask(__name__) def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort, 'double_2') @app.route('/') def index(): data_list = [1, 2, 5, 8] return render_template('index4.html', data_list=data_list) if __name__ == "__main__": app.run()
index4.html
<p>{{ data_list | double_2}}</p>
相似于 python
中的函数,宏的做用就是在模板中重复利用代码,避免代码冗余。
定义不带参数的宏:
<!-- 定义宏 --> {% macro input() %} <input type="text" name="username" value="" size="30"/> {% endmacro %} <!-- 调用宏 --> {{ input() }}
定义带参数的宏:
{% macro input(name,value='',type='text',size=20) %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}" size="{{ size }}"/> {% endmacro %} <!-- 调用宏,并传递参数 --> {{ input(value='name',type='password',size=40)}}
还能够把宏单独抽取出来,封装成 html
文件,其它模板中导入使用文件名能够自定义macro.html
:
{% macro function() %} <input type="text" name="username" placeholde="Username"> <input type="password" name="password" placeholde="Password"> <input type="submit"> {% endmacro %}
在其它模板文件中先导入,再调用:
{% import 'macro.html' as func %} {% func.function() %}
宏、继承(extend)和包含(include)区别:
{{ config.SQLALCHEMY_DATABASE_URI }}
get_flashed_messages
方法:应用在模板中通常用于获取错误信息,返回以前在Flask中经过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,而后经过调用 get_flashed_messages() 方法取出。{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
一、请求 request
浏览器发送过来的 HTTP 请求,被 flask 封装在 request中(werkzeug.wrappers.BaseRequest)
中,提供了一下字段或方法以供使用:
request.method # 请求方法 request.args # 获取GET请求参数 request.form # 获取POST请求参数 request.values request.files # 获取上传文件 request.cookies # cookies request.headers # 请求头 request.path request.full_path request.script_root request.url # 请求 URL request.base_url # 获取请求路径 request.url_root request.host_url request.host # 获取ip和端口
二、响应 response
一、模板渲染、重定向
模板渲染:render_template
、重定向:redirect
、url_for
from flask import Flask, render_template, request, redirect, url_for app = Flask(__name__) @app.route('/index/', method=['GET', 'POST']) def index(): return render_template('index.html') # redirect(url_for('login')) # 重定向
二、错误信息
from flask import Flask, abort, render_template app = Flask(__name__) @app.route('/index/', methods=['GET', 'POST']) def index(): abort(404, 'Page Not Found!') # 返回一个 Page Not Found! 的页面 if __name__ == '__main__': app.run()
三、make_reponse
make_response
相似于 Django 的 HTTPResponse
,用于返回字符串,下面是几个比较常见的用法:
返回内容、页面:
from flask import Flask, abort, render_template, make_response app = Flask(__name__) def func(): return '<h1>Flask 自定义模板</h1>' @app.route('/index/', methods=['GET', 'POST']) def index(): # return make_response('make response') # 返回内容 response = make_response(render_template('index.html', func=func)) # 返回页面 # return response, 200, # 返回内容和状态码
response
是 flask.wrappers.Response
类型,同时 make_resposne
还能够用来设置响应头、设置 cookie 等:
# response.delete_cookie # response.set_cookie # response.headers['X-Something'] = 'A value'
@app.route('/index/', methods=['GET', 'POST']) def index(): response = make_response(render_template('index.html', func=func)) response.headers['hahaha'] = 'hello world' return response
若是在浏览器中输入不可用的路由,那么就会显示一个 404 状态的错误页面,这个页面太过简陋、平庸,并非咱们所但愿的。好在 Flask 容许咱们基于模板自定义错误页面,须要用到模块:flask-bootstrap
。
安装:
pip3 install flask-bootstrap
下面来示范两个最多见的错误:404 和 500:
一、app.py
初始化 bootstrap
,定义两个函数,用于处理 404 和 500 错误页面:
from flask import Flask, abort, render_template from flask_bootstrap import Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app) # 初始化,千万别忘记 @app.route('/index/', methods=['GET', 'POST']) def index(): return "OK" @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 if __name__ == '__main__': app.run()
二、在模板中定义一个母版 templates/errors/base.html
,编辑以下:
{%extends "bootstrap/base.html"%} {%block title %}Flask{% endblock %} {%block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> <div class="page-header"> {% block page_content %}{% endblock %} </div> </div> {% endblock %}
三、下面再编写 404.html
和 500.html
404.html
{% extends "base.html" %} {% block title %}Flasky - Page Not Found{% endblock %} {% block page_content %} <div class="page-header"> <h1>Not Found</h1> </div> {% endblock %}
500.html
{% extends "base.html" %} {% block title %}Flasky - internal server error{% endblock %} {% block page_content %} <div class="page-header"> <h1>internal server error</h1> </div> {% endblock %}
如今咱们来访问一个不存在的页面看看:http://127.0.0.1:5000/main/
彷佛好看了点,固然你还能够定制更为高端一点。
Session 依赖于 Cookies ,而且对 Cookies 进行密钥签名要使用会话,你须要设置一个密钥。
session['username'] = 'xxx'
session.pop('username', None)
session.get('username')
设置获取 Cookie
from flask import Flask,make_response @app.route('/cookie') def set_cookie(): resp = make_response('this is to set cookie') resp.set_cookie('username', 'itcast') return resp from flask import Flask,request #获取cookie @app.route('/request') def resp_cookie(): resp = request.cookies.get('username') return resp
示例:利用 Session 验证用户登陆
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route('/') def index(): if 'username' in session: return '欢迎回来: %s' % escape(session['username']) return '你没有登陆' # 也能够重定向到登陆页面 @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ''' @app.route('/logout') def logout(): # 从 session 移除 username session.pop('username', None) return redirect(url_for('index')) # 设置密钥 app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' if __name__ == '__main__': app.run()
message 是一个基于 Session 实现的用于保存数据的集合,其特色是:使用一次就删除
app.py
from flask import Flask, flash, redirect, render_template, request app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/') def index1(): return render_template('index2.html') @app.route('/set') def index2(): v = request.args.get('p') flash(v) # 将消息给下一个请求,并将其删除,模板必须调用 get_flashed_messages() 函数 return 'ok' if __name__ == "__main__": app.run()
index2.html
模板中必须使用 get_flashed_messages()
才能获取到 message
传递的信息:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>messages</title> </head> <body> {% with messages = get_flashed_messages() %} <!-- with xxx:取别名 --> {% if messages %} <ul class=flashes> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} </body> </html>
首先请求:http://127.0.0.1:5000/set?p=123
,index()
函数获取 p 的值,并将其传递给下一个请求(传递后即将其删除)。
第二个请求访问:http://127.0.0.1:5000/
,从而得到上一次请求传递的信息:
所谓请求钩子便是用来处理请求前、后,一些特定事情的功能实现。如:请求前链接数据库、请求后指定数据交互格式等。Flask 中请求钩子以装饰器形式实现:
Flask 中请求钩子:
示例:基于 before_request
实现用户登陆认证
相比较装饰器而已有以下优点:
before_request
,只需一个便可。from flask import Flask, render_template, request, redirect, session, url_for app = Flask(__name__) app.secret_key = "123$456" # 基于flask里请求扩展来作 @app.before_request def process_request(*args, **kwargs): """无论访问哪一个视图函数,都会实现执行这个函数,也就意味着每次都会检查是否登陆""" # 访问/login的时候尚未登陆,就会一直重定向到登陆页,因此就要设置个白名单,若是请求地址是/login,就返回None if request.path == "/login": return None # 1.登陆验证功能 user = session.get('user_info') # 2.若是登陆信息正常,什么都不作,程序继续其余执行 if user: return None # 3.若是登陆验证不经过,就重定向到登陆页面 return redirect("/login") @app.route("/index", methods=['GET']) def index(): name = session.get('user_info', None) return 'Welcome, %s' % name @app.route("/login", methods=['GET', 'POST']) def login(): if request.method == "GET": return render_template("login.html") else: user = request.form.get("username") pwd = request.form.get("password") if user == "rose" and pwd == "123456": session['user_info'] = user return redirect("/index") return render_template("login.html", **{"error": "用户名和密码错误"}) if __name__ == "__main__": app.run()
也能够有多个请求钩子,可是要注意的是当有多个请求中间件时,执行顺序是不一样的:
before_request
:从上到下按照顺序执行after_request
:从下到上的顺序执行before_request
时,若第一个有 return
语句,那么后面的 before_request
将被拦截不被执行,可是 after_request
会继续执行Flask 程序在运行过程当中会事先加载 wsgi_app
,基于此咱们能够拓展作些别的事情:
from flask import Flask app = Flask(__name__) @app.route("/login", methods=['GET', 'POST']) def index(): pass class Md(object): def __init__(self, old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): print("开始以前") ret = self.old_wsgi_app(environ, start_response) print("结束以后") return ret if __name__ =="__main__": app.wsgi_app = Md(app.wsgi_app) # 至关于把wsgi_app给更新了 app.run()
表单由三个部分组成:表单标签、表单域、表单按钮。Flask 经过Flask-WTF 实现表单功能。它封装了WTForms,能够生成表单、以及验证表单数据。
安装:
pip3 install Flask-WTF pip3 install flask-moment
WTForms 支持的 HTML 标准字段:
StringField # 文本字段 TextAreaField # 多行文本字段 PasswordField # 密码文本字段 HiddenField # 隐藏文本字段 DateField # 文本字段,值为datetime.date格式 DateTimeField # 文本字段,值为datetime.datetime格式 IntegerField # 文本字段,值为整数 DecimalField # 文本字段,值为decimal.Decimal FloatField # 文本字段,值为浮点数 BooleanField # 复选框,值为True和False RadioField # 一组单选框 SelectField # 下拉列表 SelectMultipleField # 下拉列表,可选择多个值 FileField # 文本上传字段 SubmitField # 表单提交按钮 FormField # 把表单做为字段嵌入另外一个表单 FieldList # 一组指定类型的字段
WTForms 经常使用验证函数:
验证函数 | 说明 |
---|---|
DataRequired | 确保字段中有数据 |
AEqualTo | 比较两个字段的值,经常使用于比较两次密码输入 |
Length | 验证输入的字符串长度 |
NumberRange | 验证输入的值在数字范围内 |
URL | 验证URL |
AnyOf | 验证输入值在可选列表中 |
NoneOf | 验证输入值不在可选列表中 |
跨站请求伪造保护 csrf
设置一个密匙,生成加密令牌,再用令牌验证请求表单中数据真伪:
app = Flask(__name__) app.config['SECRET_KEY'] = 'hard to guess string' # 密匙随便取 # 若是你设置的模板中存在表单,你只须要在表单中添加以下 <form method="post" action="/"> {{ form.csrf_token }} </form> # 若是没有模板中没有表单,你仍然须要一个 CSRF 令牌 <form method="post" action="/"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> </form>
简单示例
一、定义一个表单类:
from flask_wtf import FlaskForm #导入自定义表单须要的字段 from wtforms import SubmitField, StringField, PasswordField #导入wtf扩展提供的表单验证器 from wtforms.validators import DataRequired, EqualTo # validators 不能为空 class NameForm(FlaskForm): name = StringField(label='用户名', validators=[Required()]) submit = SubmitField('提交')
二、在模板中使用表单:
<h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1> <form method='post'> {{ form.hidden_tag() }} {{ form.name.label }} {{form.name() }} {{ form.submit() }} # 传入 HTML 属性 {{ form.name.label }} {{form.name(id='id_name') }} </form>
老是本身传属性太麻烦,并且表单样式过于丑陋。能够用 flask-bootstrap
,它是对 bootstrap
进行了简单封装,安装好以后能够直接调用:
<!--上面表单可用下面方式一次渲染--> {% import 'bootstrap/wtf.html' as wtf %} {{ wtf.quick_form(form) }}
from flask_bootstrap import Bootstrap app = Flask(__name__) bootstrap = Bootstrap(app)
wtf.quick_form(form)
函数参数为 Flask-WTF
表单对象
<!-- 条件结果为 True,渲染 if else 中文字,不然渲染 else endif 中语句 --> <h1>Hello, {% if name %}{{ name }} {% else %} Stranger {% endif %}!</h1>
实现方式:继承+WTForms +Flask-Bootstrap
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash from flask_bootstrap import Bootstrap from flask_moment import Moment #导入wtf扩展的表单类 from flask_wtf import FlaskForm #导入自定义表单须要的字段 from wtforms import SubmitField, StringField, PasswordField #导入wtf扩展提供的表单验证器 from wtforms.validators import DataRequired, EqualTo app = Flask(__name__) app.config['SECRET_KEY'] = '1' bootstrap = Bootstrap(app) # 使用 bootstrap 渲染表单 moment = Moment(app) # 本地化时间 #自定义表单类,文本字段、密码字段、提交按钮 class Login(FlaskForm): user = StringField(label=u'用户:', validators=[DataRequired()]) pwd1 = PasswordField(label=u'密码', validators=[DataRequired()]) pwd2 = PasswordField(label=u'确认密码', validators=[DataRequired()]) submit = SubmitField(u'提交') @app.route('/', methods=['GET','POST']) def index(): return render_template('index.html') #定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证 @app.route('/login', methods=['GET','POST']) def login(): form = Login() if form.validate_on_submit(): name = form.user.data pwd1 = form.pwd1.data pwd2 = form.pwd2.data print(name, pwd1, pwd2) if name == 'rose' and (pwd1 == pwd2 and pwd2 == '123'): return redirect(url_for('index')) else: flash(u'用户名或密码错误!') return render_template('login.html', form=form) if __name__ == '__main__': app.run()
include/base.html
{% extends "bootstrap/base.html" %} {% block title %}Flask WTF{% endblock %} {% block head %} {{ super() }} <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon"> <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon"> {% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flask WTF</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> <!-- get_flashed_messages() 显示错误信息 --> {% for message in get_flashed_messages() %} <div class="alert alert-warning"> <button type="button" class="close" data-dismiss="alert">×</button> {{ message }} </div> {% endfor %} {% block page_content %}{% endblock %} </div> {% endblock %} {% block scripts %} {{ super() }} {{ moment.include_moment() }} {% endblock %}
login.html
继承 base.html
{% extends "include/base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flask WTF{% endblock %} {% block page_content %} <div class="page-header"> </div> {{ wtf.quick_form(form) }} {% endblock %}
将用户我的信息保存到会话中:
app.py
from flask import Flask, render_template, redirect, url_for, session, request,flash def login('/login', methods=['GET', 'POST']): ... # 会话中原有值 old_name = session.get('name') if old_name is not None and old_name != form.name.data: flash('用户名错误!') session['name'] = form.name.data return redirect(url_for('index')) return render_template('login.html', form=form, name=session.get('name'))
login.html
<div class="page-header"> <h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div>