WEB框架之Flask
前言:
Django:1个重武器,包含了web开发中常用的功能、组件的框架;(ORM、Session、Form、Admin、分页、中间件、信号、缓存、ContenType....);
Tornado:2大特性就是异步非阻塞、原生支持WebSocket协议;
Flask:功能和性能虽然不及Django和Tornado,但是Flask的第三方开源组件比丰富;http://flask.pocoo.org/extensions/
Bottle:比较简单;
总结:
都不是我写的!!!不论优劣,不同的工具而已;
小型web应用设计的功能点不多使用Flask;
大型web应用设计的功能点比较多使用的组件也会比较多,使用Django(自带功能多不用去找插件);
如果追求性能可以考虑Tornado;
Flask的socket是基于Werkzeug 实现的,模板语言依赖jinja2模板,在使用Flask之前需要安装一下;
pip3 install flask #安装flask
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from werkzeug.wrappers import Request, Response # Flask的socket使用werkzeug实现,所以要导入 werkzeug @Request.application def hellow(request): return Response('Hello World') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost',400,hellow)
Flask简单使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask app=Flask(__name__) #创建1个Flask实例 @app.route('/') #路由系统生成 视图对应url,1. decorator=app.route() 2. decorator(first_flask) def first_flask(): #视图函数 return 'Hello World' #response if __name__ == '__main__': app.run() #启动socket
一、配置文件
app=Flask(__name__,template_folder='templates',static_url_path='/static/',static_path='/zhanggen')
模板路径: template_folder='templates'
静态文件路径:static_url_path='/static/'
静态文件引入别名:static_path='/zhanggen'
设置为调试环境:app.debug=True (代码修改自动更新)
设置json编码格式 如果为False 就不使用ascii编码:app.config['JSON_AS_ASCII']=False
设置响应头信息Content-Type app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" (注意 ;charset=utf-8)
二、路由系统
1.动态路由(url传参)
@app.route('/user/<name>')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask app=Flask(__name__) @app.route('/<name>') #设置url传参数 http://127.0.0.1:5000/zhanggen def first_flask(name): #视图必须有对应接收参数 print(name) return 'Hello World' #response if __name__ == '__main__': app.run()
@app.route('/post/<int:age>')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#接收整型数字参数 app=Flask(__name__) @app.route('/<int:age>/') #设置url传参数 http://127.0.0.1:5000/18/ def first_flask(age): #视图必须有对应接收参数 print(age) return 'Hello World' #response if __name__ == '__main__': app.run()
@app.route('/post/<float:salary>')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#接收浮点型型数字参数 app=Flask(__name__) @app.route('/<float:salary>/') #设置url传参数http://127.0.0.1:5000/2345555.8889/ def first_flask(salary): #视图必须有对应接收参数 print(salary) return 'Hello World' #response if __name__ == '__main__': app.run()
@app.route('/post/<path:path>')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 接收URL链接类型参数 app=Flask(__name__) @app.route('/<path:url>/') #设置url传参数:http://127.0.0.1:5000/http://www.baiu.com/ def first_flask(url): #视图必须有对应接收参数 print(url) return 'Hello World' #response if __name__ == '__main__': app.run()
2、指定允许的请求方法
@app.route('/login', methods=['GET', 'POST'])
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 指定允许的请求方法 app=Flask(__name__) @app.route('/<path:url>/',methods=['get']) #只允许get请求 def first_flask(url): print(url) return 'Hello World' #response if __name__ == '__main__': app.run()
3、通过别名反向生成url
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#反向生成url from flask import Flask,url_for app=Flask(__name__) @app.route('/<path:url>',endpoint='name1') def first_flask(url): print(url_for('name1',url=url)) #如果设置了url参数,url_for(别名,加参数) return 'Hello World' if __name__ == '__main__': app.run()
4、通过app.add_url_rule()调用路由
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#方式2通过app.add_url_rule()方法的方式调用路由 app=Flask(__name__) def first_flask(): return 'Hello World' app.add_url_rule(rule='/index/',endpoint='name1',view_func=first_flask,methods=['GET']) #app.add_url_rule(rule=访问的url,endpoint=路由别名,view_func=视图名称,methods=[允许访问的方法]) if __name__ == '__main__': app.run()
5、扩展路由功能:正则匹配url
如果需要一些复杂的匹配规则可以自定义正则匹配url
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
四、视图
1、给Flask视图函数加装饰器
注意如果要给视图函数加装饰器增加新功能,一点要加在路由装饰器下面,才会被路由装饰器装饰,才能生生成url关系;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#给Flask视图加装饰器 #1、定义1个装饰器 def auth(func): print('我在上面') def inner(*args,**kwargs): return func(*args,**kwargs) return inner app=Flask(__name__) @app.route('/',methods=['GET']) @auth #注意如果要给视图函数加装饰器,一点要加在路由装饰器下面,才会被路由装饰器装饰 def first_flask(): print('ffff') return 'Hello World' if __name__ == '__main__': app.run()
2、request和response
a.请求相关信息
request.method: 获取请求方法
request.json
request.json.get("json_key"):获取json数据 **较常用
request.argsget('name') :获取get请求参数
request.form.get('name') :获取POST请求参数
request.form.getlist('name_list'):获取POST请求参数列表(多个)
request.values.get('age') :获取GET和POST请求携带的所有参数(GET/POST通用)
request.cookies.get('name'):获取cookies信息
request.headers.get('Host'):获取请求头相关信息
request.path:获取用户访问的url地址,例如(/,/login/,/ index/);
request.full_path:获取用户访问的完整url地址+参数 例如(/login/?age=18)
request.script_root: 抱歉,暂未理解其含义;
request.url:获取访问url地址,例如http://127.0.0.1:5000/?age=18;
request.base_url:获取访问url地址,例如 http://127.0.0.1:5000/;
request.url_root
request.host_url
request.host:获取主机地址
request.files:获取用户上传的文件
obj = request.files['the_file_name']
obj.save('/var/www/uploads/' + secure_filename(f.filename)) 直接保存
b、响应相关信息
return "字符串" :响应字符串
return render_template('html模板路径',**{}):响应模板
return redirect('/index.html'):跳转页面
响应json数据
方式1: return jsonify(user_list)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码, app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;
方式2:
return Response(data,mimetype="application/json;charset=utf-8",)
如果需要设置响应头就需要借助make_response()方法
from flask import Flask,request,make_response
response = make_response(render_template('index.html'))
response是flask.wrappers.Response类型
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return respons
3 、Flask之CBV视图
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#CBV视图 from flask import Flask,url_for,views #----------------------------------------------------- app=Flask(__name__) #装饰器 def auth(func): print('我在上面') def inner(*args,**kwargs): return func(*args,**kwargs) return inner #-------------------------------------------------------- class IndexView(views.MethodView): #CBV视图 methods=['GET'] #允许的http请求方法(改CBV只允许GET方法) decorators = [auth,] #每次请求过来都加auth装饰器 def get(self): return 'Index.GET' def post(self): return 'Index.POST' app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url别名 if __name__ == '__main__': app.run()
五、模板语言
Flask使用的是Jinja2模板,所以其语法和Django无差别(Django的模板语言参考Jinja2)
1.引用静态文件
方式1:别名引入
<link rel="stylesheet" href="/zhanggen/commons.css">
方式2:url_for()方法引入
<link rel="stylesheet" href="{{ url_for('static',filename='commons.css') }}">
2.模板语言引用上下文对象
变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<h1>{{user_list}}</h1> <!--变量 -->
循环、索引取值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<ul> {% for user in user_list %} <!--循环 --> <li>{{user}}</li> {% endfor %} {{user_list.0}} <!-- 索引取值--> </ul>
Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter;
simple_tag(只能传2个参数,支持for、if)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@app.template_global() #simple_tag def foo(arg): return '<input type="text">'
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<h1>{{foo(1)|safe}}</h1> <!--Flask的模板语言支持simple_tag-->
simple_fifter(对参数个数无限制,不支持for、if)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@app.template_filter() #simple_fifter def foo1(arg1,arg2,arg3): return arg1+arg2+arg3
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<h1> {{ 'alex'|foo1('s ','b',) }} </h1> <!-- simple_fifter -->
六、session功能
1. Flask自带的session功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import session import json app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/') app.debug=True app.secret_key='sjehfjeefrjewth43u' #设置session加密 app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码, app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式; @app.route('/login/',methods=['GET','POST']) def login(): msg = '' if request.method=='POST': name=request.values.get('user') pwd=request.values.get('pwd') if name =='zhanggen' and pwd=='123.com': session['user']=name #设置session的key value return redirect('/index/') else: msg='用户名或者密码错误' return render_template('login.html',msg=msg) @app.route('/index/',methods=['GET','POST']) def index(): user_list = ['张根', 'egon', 'eric'] user=session.get('user') #获取session if user: user=['alex','egon','eric'] return jsonify(user_list) else: return redirect('/login/') if __name__ == '__main__': app.run()
2.第三方session组件(Session)
安装 pip install flask-session
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import session, Flask,request,make_response,render_template,redirect,jsonify,Response from flask.ext.session import Session #引入第三方session import json app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/') app.debug=True app.secret_key='sjehfjeefrjewth43u' #设置session加密 app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码, app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式; app.config['SESSION_TYPE']='redis' from redis import Redis #引入连接 redis模块 app.config['SESSION_REDIS']=Redis(host='192.168.0.94',port=6379) #连接redis Session(app) @app.route('/login/',methods=['GET','POST']) def login(): msg = '' if request.method=='POST': name=request.values.get('user') pwd=request.values.get('pwd') if name =='zhanggen' and pwd=='123.com': session['user']=name #设置session的key value return redirect('/index/') else: msg='用户名或者密码错误' return render_template('login.html',msg=msg) @app.route('/index/',methods=['GET','POST']) def index(): user_list = ['张根', 'egon', 'eric'] user=session.get('user') #获取session if user: user=['alex','egon','eric'] return jsonify(user_list) else: return redirect('/login/') if __name__ == '__main__': app.run()
不仅可以把session存放到redis还可放到文件、内存、memcache...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def _get_interface(self, app): config = app.config.copy() config.setdefault('SESSION_TYPE', 'null') config.setdefault('SESSION_PERMANENT', True) config.setdefault('SESSION_USE_SIGNER', False) config.setdefault('SESSION_KEY_PREFIX', 'session:') config.setdefault('SESSION_REDIS', None) config.setdefault('SESSION_MEMCACHED', None) config.setdefault('SESSION_FILE_DIR', os.path.join(os.getcwd(), 'flask_session')) config.setdefault('SESSION_FILE_THRESHOLD', 500) config.setdefault('SESSION_FILE_MODE', 384) config.setdefault('SESSION_MONGODB', None) config.setdefault('SESSION_MONGODB_DB', 'flask_session') config.setdefault('SESSION_MONGODB_COLLECT', 'sessions') config.setdefault('SESSION_SQLALCHEMY', None) config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions') if config['SESSION_TYPE'] == 'redis': session_interface = RedisSessionInterface( config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'memcached': session_interface = MemcachedSessionInterface( config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'filesystem': session_interface = FileSystemSessionInterface( config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'], config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'mongodb': session_interface = MongoDBSessionInterface( config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'], config['SESSION_MONGODB_COLLECT'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) elif config['SESSION_TYPE'] == 'sqlalchemy': session_interface = SqlAlchemySessionInterface( app, config['SESSION_SQLALCHEMY'], config['SESSION_SQLALCHEMY_TABLE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT']) else: session_interface = NullSessionInterface() return session_interface
3.自定义session组件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json from flask.sessions import SessionInterface from flask.sessions import SessionMixin from itsdangerous import Signer, BadSignature, want_bytes class MySession(dict, SessionMixin): def __init__(self, initial=None, sid=None): self.sid = sid self.initial = initial super(MySession, self).__init__(initial or ()) def __setitem__(self, key, value): super(MySession, self).__setitem__(key, value) def __getitem__(self, item): return super(MySession, self).__getitem__(item) def __delitem__(self, key): super(MySession, self).__delitem__(key) class MySessionInterface(SessionInterface): session_class = MySession container = {} def __init__(self): import redis self.redis = redis.Redis() def _generate_sid(self): return str(uuid.uuid4()) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='flask-session', key_derivation='hmac') def open_session(self, app, request): """ 程序刚启动时执行,需要返回一个session对象 """ sid = request.cookies.get(app.session_cookie_name) if not sid: sid = self._generate_sid() return self.session_class(sid=sid) signer = self._get_signer(app) try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid) # session保存在redis中 # val = self.redis.get(sid) # session保存在内存中 val = self.container.get(sid) if val is not None: try: data = json.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid=sid) return self.session_class(sid=sid) def save_session(self, app, session, response): """ 程序结束前执行,可以保存session中所有的值 如: 保存到resit 写入到用户cookie """ domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) # session保存在redis中 # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime) # session保存在内存中 self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask from flask import session from my_session import MySessionInterface app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' app.session_interface = MySessionInterface() @app.route('/login/', methods=['GET', "POST"]) def login(): print(session) session['user1'] = 'alex' session['user2'] = 'alex' del session['user2'] return "内容" if __name__ == '__main__': app.run()
七、蓝图
使用Flask自带Blueprintmuk模块,帮助我们做代码目录结构的归类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import luffy #导入luffy包就会执行luffy包中__init__.py文件 luffy.app.run()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/') app.debug=True from .views import login from .views import index #把文件中蓝图对象注册到app里 app.register_blueprint(login.login,url_prefix='/login') #访问login蓝图必须以url_prefix开头 app.register_blueprint(index.index,url_prefix='/index') if __name__ == '__main__': app.run()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Blueprint #导入蓝图 login=Blueprint('login',__name__) #在本模块实例化1个蓝图 @login.route('/login/',methods=['GET','POST']) def login1(): return '登录页面'
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Blueprint index=Blueprint('index',__name__) @index.route('/index/',methods=['GET','POST']) def index1(): return '首页'
八、message (闪现)
message是一个基于Session实现的用于保存数据的集合,其特点是:一次性。
特点:和labada匿名函数一样不长期占用内存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from flask import Flask,request,flash,get_flashed_messages app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/set/') def index2(): flash('Disposable') #在message中设置1个个值 return 'ok' #--------------------------------------------------------------------------------- @app.route('/') def index1(): messages = get_flashed_messages() #获取message中设置的值,只能获取1次。(1次性) print(messages) return "Index1" if __name__ == "__main__": app.run()
九、中间件
flask也有中间件功能和Django类似,不同的是使用的是使用3个装饰器来实现的;
1.@app.before_first_request :请求第1次到来执行1次,之后都不执行;
2.@app.before_request:请求到达视图之前执行;(改函数不能有返回值,否则直接在当前返回)
3.@app.after_request:请求 经过视图之后执行;(最下面的先执行)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates') app.debug = True @app.before_first_request #第1个请求到来执行 def before_first_request1(): print('before_first_request1') @app.before_request #中间件2 def before_request1(): Request.nnn = 123 print('before_request1') #不能有返回值,一旦有返回值在当前返回 @app.before_request def before_request2(): print('before_request2') @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.route('/') def hello_world(): return "Hello World" @app.after_request #中间件 执行视图之后 def after_request1(response): print('after_request1', response) return response @app.after_request #中间件 执行视图之后 先执行 after_request2 def after_request2(response): print('after_request2', response) return response if __name__ == '__main__': app.run()
十、Flask插件
WTForms
SQLAchemy
等... 详细见:http://flask.pocoo.org/extensions/
文档:http://docs.jinkan.org/docs/flask/quickstart.html
银角大王:http://www.cnblogs.com/wupeiqi/articles/7552008.html
银角大王:http://www.cnblogs.com/wupeiqi/articles/5713330.html(执行原生SQL模块 pymsql ,ORM框架 SQLAchemy)