Flask是一个基于python依赖jinjia2模板和Werkzeug WSGI 服务的一个微型框架. werkzeug和Django中的wsgiref模块一样其本质就是一个socket服务端,,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
Werkzeug 并不是 一个框架,它是一个 WSGI 工具集的库,你可以通过它来创建你自己的框架或 Web 应用
Flask 的特点: 短小精悍,易于扩展第三方组件丰富.
Flask是一个微框架,
微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
flask和djano的区别?
djano大而全,无socket,借助第三方模块wsgiref模块,内部提供: ORM admin 中间件,Form,ModelForm,session,缓存,信号CSRF
Flask ,短小精悍,易于扩展,无socket,借助第三方库Werkzeug.第三方组件丰富
Flask的简单应用
#一个基于werkzeug库的Wsgi应用应该是这样的 from werkzeug.wrappers import Request,Response from werkzeug.serving import run_simple @Request.application def hello(request): return Response('Hello,werkzeug') if __name__ == '__main__': run_simple('localhost',4000,hello)
Flask就是基于werkzeug来开发应用程序的
#简单的Flask应用 hello from flask import Flask#导入Flask类 app=Flask(__name__)#实例化一个对象,这个对象就是我们的W基于WSGI的应用程序,括号里的参数是给对象起的名字,随便一个字符串就可以,__name__表示的是模块名字的字符串 @app.route('/index') #router函数把url和视图函数捆绑起来 def index(): return 'Hello Flask' if __name__ == '__main__': print(type(__name__)) app.run(debug=True)#相当于run_simple('localhost',5000,index),
调式模式
虽然 run() 方法适用于启动本地的开发服务器,但是你每次修改代码后都要手动重启它。这样并不够优雅,
有两种方法来改变这个弊端
方法一: 直接在应用对象上设置
if __name__ == '__main__': app.debug=True app.run()#相当于run_simple('localhost',5000,index),
方法二: 在run函数中设置
app.run(debug=True)
配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: 2 { 3 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式 4 'TESTING': False, 是否开启测试模式 5 'PROPAGATE_EXCEPTIONS': None, 6 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 7 'SECRET_KEY': None, 8 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 9 'USE_X_SENDFILE': False, 10 'LOGGER_NAME': None, 11 'LOGGER_HANDLER_POLICY': 'always', 12 'SERVER_NAME': None, 13 'APPLICATION_ROOT': None, 14 'SESSION_COOKIE_NAME': 'session', 15 'SESSION_COOKIE_DOMAIN': None, 16 'SESSION_COOKIE_PATH': None, 17 'SESSION_COOKIE_HTTPONLY': True, 18 'SESSION_COOKIE_SECURE': False, 19 'SESSION_REFRESH_EACH_REQUEST': True, 20 'MAX_CONTENT_LENGTH': None, 21 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 22 'TRAP_BAD_REQUEST_ERRORS': False, 23 'TRAP_HTTP_EXCEPTIONS': False, 24 'EXPLAIN_TEMPLATE_LOADING': False, 25 'PREFERRED_URL_SCHEME': 'http', 26 'JSON_AS_ASCII': True, 27 'JSON_SORT_KEYS': True, 28 'JSONIFY_PRETTYPRINT_REGULAR': True, 29 'JSONIFY_MIMETYPE': 'application/json', 30 'TEMPLATES_AUTO_RELOAD': None, 31 }
如果我们要更改配置文件的话用这种方法
settings.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): """ 生产环境 """ DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): """ 开发环境 """ DEBUG = True class TestingConfig(Config): """ 测试环境 """ TESTING = True #PS: 从sys.path中已经存在路径开始写 #PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
flask_demo.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from flask import Flask app=Flask(__name__) app.config.from_object('seetings.DevelopmentConfig') #直接导入配置文件中的类就可以了 @app.route('/index') def index(): return 'Hello Flask' if __name__ == '__main__': print(type(__name__)) app.run()
路由
route() 装饰器把一个函数绑定到对应的 URL 上。
添加路由的两种方式:
# 路由方式一(*):装饰器 @app.route('/index',methods=['GET','POST']) #当有post提交时 def index(): return "Index"
#路由方式二 路由方式一种的装饰器也是用的这种方法 def order(): return 'Order' app.add_url_rule('/order',view_func=order)#view_func绑定那个函数
给url传参数:
要给 URL 传参数,你可以把这些特殊的字段标记为 <variable_name> , 这个部分将会作为命名参数传递到你的函数。
格式为: <转换器:参数名称> 转换器(其实就是要求参数的格式类型)。
from flask import Flask app=Flask(__name__) @app.route('/index/<int:id>/') #参数id要求为int类型 def index(id):#别忘记传参 return 'Hello Flask%s'%(id) if __name__ == '__main__': app.debug=True app.run()
转换器有下面几种:
@app.route('/user/<username>') #常用的 不加参数的时候默认是字符串形式的 @app.route('/post/<int:post_id>') #常用的 #指定int,说明是整型的 @app.route('/post/<float:post_id>') #浮点
@app.route('/post/<path:path>') #路径,可以拼接路径 @app.route('/login', methods=['GET', 'POST'])
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
router中的url一个/和两个/的区别?
唯一 URL / 重定向行为 Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。 以这两个规则为例: @app.route('/projects/') def projects(): return 'The project page' @app.route('/about') def about(): return 'The about page' 虽然它们看起来着实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,指向 projects 的规范 URL 尾端有一个斜线。这种感觉很像在文件系统中的文件夹。
访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。 然而,第二种情况的 URL 结尾不带斜线,类似 UNIX-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。 这个行为使得在遗忘尾斜线时,允许关联的 URL 接任工作,与 Apache 和其它的服务器的行为并无二异。此外,也保证了 URL 的唯一,有助于避免搜索引擎索引同一个页面两次。
构造url(反向解析)
当视图函数中用到了url,不管url怎么变化,视图中的url都会反向解析到正确的url
注意:endpoint不写,别名默认视图函数名字
from flask import Flask,url_for#反向解析需要导入url_for 相当于Django中的reverse app=Flask(__name__) @app.route('/aa/',endpoint='index') #endpoint相当于django中的name,主要是起别名的作用 def index(): v=url_for('login')#反向解析 print(v)#打印 return '这是index页面' @app.route('/login/',endpoint='login') def login(): return '这是登录页面' if __name__ == '__main__': app.run(debug=True)
结果:
/login/
自定义正则转换器
当工作中遇到url中传递正则表达式的情况,我们可以自定义正则表达式转换器
from flask import Flask ,render_template,redirect,url_for from werkzeug.routing import BaseConverter #自定义正则表达式就要继承BaseConverter这个类 app=Flask(__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['regs']=RegexConverter
@app.route('/index/<regs("\d+"):nid>',methods=['GET','POST']) #使用自定义的转换器 def index(nid): print(nid,type(nid)) v=url_for("index",nid=555)#相当于/index/555 print(v) return "这是Index页面" if __name__ == '__main__':
重定向
当需要重定向时.可以用rediret_to参数
from flask import Flask,render_template,redirect app = Flask(__name__) @app.route('/index',methods=['GET','POST'],redirect_to='/new') def index(): return "老功能" @app.route('/new',methods=['GET','POST']) def new(): return '新功能' if __name__ == '__main__': app.run()
获取子域名
首先我们要知道:域名是由子域名和顶级域名构成的.
一级域名:又叫顶级域名,一串字符串中间一个点隔开,例如baidu.com,这里说明一下,www.baidu.com不是一级域名!!而是二级域名!
二级域名:实际上就是一个一级域名以下的主机名,一串字符串中间两个“.”隔开,例如pan.baidu.com("pan"就是主机名)。
三级域名:二级域名的子域名,特征是包含三个“.”,一般来说三级域名都是免费的。
#这是顶级域名 https://baidu.com/ #这是顶级域名的子域名
https://www.baidu.com/
https://pan.baidu.com
from flask import Flask,url_for#反向解析需要导入url_for 相当于Django中的reverse app=Flask(__name__) @app.route('/aa/',subdomain="admin") def index(): v=url_for('login')#反向解析 print(v) return '这是index页面'
@app.router中的参数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@app.route和app.add_url_rule参数: rule, URL规则 view_func, 视图函数名称 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') methods=None, 允许的请求方式,如:["GET","POST"] strict_slashes=None, 对URL最后的 / 符号是否严格要求, 如: @app.route('/index',strict_slashes=False), 访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可 @app.route('/index',strict_slashes=True) 仅访问 http://www.xx.com/index redirect_to=None, 重定向到指定地址 如: @app.route('/index/<int:nid>', redirect_to='/home/<nid>') 或 def func(adapter, nid): return "/home/888" @app.route('/index/<int:nid>', redirect_to=func) subdomain=None, 子域名访问 from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'haiyan.com:5000' @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "admin.xxx.com" #动态生成 @app.route("/dynamic", subdomain="<username>") def username_index(username): """Dynamic subdomains are also supported Try going to user1.your-domain.tld/dynamic""" return username + ".your-domain.tld" if __name__ == '__main__': app.run()
视图函数中添加装饰器
其实Flask我们用的最多的就是FBV,CBV用的很少,
这里简单介绍下CBV
from flask import Flask, url_for,views app = Flask(__name__) def wapper(func): def inner(*args,**kwargs): print('before') return func(*args,**kwargs) return inner class IndexView(views.MethodView): methods = ['GET'] decorators = [wapper, ] def get(self): return 'Index.GET' def post(self): return 'Index.POST' app.add_url_rule('/index', view_func=IndexView.as_view(name='index2')) # name=endpoint if __name__ == '__main__': app.run(debug=True)
我们介绍FBV模式
当我们在Flask中的视图中添加装饰器时.我们要添加的装饰器和路由装饰器会出现冲突,这时候应该办
首先我们要确定的路由装饰器一定要先放到我们的装饰器的上边,因为它要先执行,才能再执行我们的装饰器,
当我们有多个视图函数需要装饰时候,比如这样
from flask import Flask#导入Flask类 app=Flask(__name__) def wrapper(f): def inner(*args,**kwargs): print('before') return f() return inner @app.route('/index1') @wrapper def index(): return 'Hello Flask' @app.route('/login') @wrapper def login(): return 'Hello Flask' if __name__ == '__main__': print(type(__name__)) app.run(debug=True)
这时候就会出现错误
ssertionError: View function mapping is overwriting an existing endpoint function: inner
因为 endpoint默认反向生成函数名,然而,被装饰的函数的真正的函数名都是inner,所以endpoint这时候就出错了,这时候我们就需要真正的函数名,于是就用到了wraps装饰器修复
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from flask import Flask from functools import wraps #导入wraps app=Flask(__name__) def wrapper(f): @wraps(f) def inner(*args,**kwargs): print('before') return f() return inner @app.route('/index1') @wrapper def index(): return 'Hello Flask' @app.route('/login') @wrapper def login(): return 'Hello Flask' if __name__ == '__main__': print(type(__name__)) app.run(debug=True)
HTTP方法
HTTP有许多访问url的方法,在默认情况下,只支持GET方法,但是,可以定义router中的参数来实现多种HTTP方法
比如当时post请求的时候
@app.route('/login',methods=['GET','POST']) #这里的括号里就要有method参数了
请求对象(request)和响应对象
首先我们应该知道,djano中的请求数据是通过参数request传递给视图函数的,但是在Flask不是通过参数传递进来的,而是通过上下文管理
首先从 flask 模块里导入它:
from flask import request
当前请求的 HTTP 方法可通过 method 属性来访问。通过:attr:~flask.request.form 属性来访问表单数据( POST 或 PUT 请求提交的数据)。
当访问form表单数据(POST提交)时
request.form.get()
注意:不存在时会报错,要捕获keyvalue异常
当访问url参数(get提交时)
request.args.get()
注意:不存在时会报错,要捕获keyvalue异常
请求相关的方法
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
请求相关信息 # request.method 请求方法 # request.args 当get请求 # request.form 当post请求时 # request.values # request.cookies # request.headers # request.path # request.full_path # request.script_root # request.url # request.base_url # 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))
响应相关的信息
# 响应相关信息
return "字符串" #返回字符串
return render_template('html模板路径',**{}) #渲染模板
return redirect('/index.html') #重定向
return json.dumps() #返回json数据,和他一样的就是jsonify()
设置响应头和cookie
#####如何设置响应头和cookie*******
response = make_response(render_template('index.html') ) #生成对象
#response是flask.wrappers.Response类型
response.set_cookie('key', 'value') #设置cookie
response.delete_cookie('key') #删除cookie
response.headers['X-Something'] = 'A value' #设置响应头
return response #返回respon对象
补充
from urllib.parse import urlencode,quote,unquote val = "%E6%8A%8A%E5%87%A0%E4%B8%AA" print(unquote(val)) #ba上面这样的数据转换成中文 vall='我是中国人' print(quote(vall)) vdd={"name":'小红'} print(urlencode(vdd))
模板引擎
大多数和djano的模板渲染差不多
首先要建立一个名为templates的文件在根目录下,文件夹中放html
from flask import Flask,template_rendered app=Flask(__name__) @app.route('/xxx') def xxx(): return template_rendered('/index.html',msg='fdsfd') if __name__ == '__main__': print(type(__name__)) app.run(debug=True)#相当于run_simple('localhost',5000,index),
1、为了防止xss攻击,加了验证,所以页面上显示字符串的形式,解决办法,有两种方式
- 方法一在后端Markup
v5 = Markup("<input type='text' />")
-方法二 在前端
{{ v4|safe }}
2、自定义方法
def test(a,b): return a+b @app.route('/index') def index(): return render_template("index2.html",test=test) index2.html <h1>{{ test(1,2) }}</h1>
3、写一个函数在所有的页面都使用
template_global和template_filter
@app.template_global() def sb(a1, a2): return a1 + a2 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3
调用方式:{{sb(1,2)}} {{ 1|db(2,3)}}
4、模板继承:和django的一样。extents
5.后端传函数,前端使用模板语言的时候需要加括号然而djano不需要
def sb(a1, a2): content={ 'k1':lambda x:5 } return render_template('index.html',**content) #前端 {{k1()}}
模板其他问题参考django
session
它允许你在不同请求间存储特定用户的信息。
Flask中的session和djano中的session是不同的,Flask中的session放到cookie中,它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名。这意味着用户可以查看你 Cookie 的内容,但却不能修改它,除非用户知道签名的密钥。
一个完整的范例
要求 登陆后把信息添加session中
flask.py
from flask import Flask,render_template,request,redirect,session app=Flask(__name__) app.secret_key='wefjldsaj' #先要进行密钥签名 USER_DICT={ '1':{'name':'小明'}, '2':{'name':'小红'}, '3':{'name':'36'} }#模拟一个数据库 @app.route('/login',methods=['GET','POST']) def login(): print('请求来了') if request.method=="POST": username=request.form.get('username')#从form表单中取数据 password=request.form.get('password') #request.args #对应get从url中取数据 if username=='alex' and password=="123": #登录成功后把用户信息放入session中 session['user_info']=username return redirect('/index') else: return render_template('/login.html',msg="用户名和密码错误") #还可以这样传值return render_template('/login.html',**{'msg':"用户名和密码错误"}) return render_template('/login.html') @app.route('/index') def index(): """ 主页 :return: """ username=session.get('user_info') if not username: return redirect('/login') print(USER_DICT) return render_template('/index.html',user_dict=USER_DICT) @app.route('/logout') def logout(): session.pop('user_info',None) return '已经退出' if __name__ == '__main__': app.run(debug=True)
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!--IE浏览器最高渲染--> <meta name="viewport" content="width=device-width, initial-scale=1"> <!--为了确保适当的绘制和缩放--> <title>主页</title> <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css"> </head> <body> <ul> {% for k,v in user_dict.items() %} <li>{{v.name}}</li> {% endfor%} </ul> <script src="../jquery-3.2.1.min.js"></script> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!--IE浏览器最高渲染--> <meta name="viewport" content="width=device-width, initial-scale=1"> <!--为了确保适当的绘制和缩放--> <title>用户登录</title> <link rel="stylesheet" href="../bootstrap-3.3.7-dist/css/bootstrap.min.css"> </head> <body> <h1>请登录</h1> <form method="post"> 用户名:<input type="text" name="username"> 密码: <input type="password" name="password">{{msg}} <input type="submit" value="提交"> </form> <script src="../jquery-3.2.1.min.js"></script> </body> </html>
from flask import Flask,url_for,session app = Flask(__name__) app.secret_key = "sdsfdgdgdgd" app.config['SESSION_COOKIE_NAME'] = 'session_lvning' #设置session的名字 @app.route('/index/') def index(nid): #session本质上操作的是字典, session是否还有其他方法?与字典方法相同 #session的原理:如果下一次访问的时候带着随机字符串,会把session里面对应的 # 值拿到内存,假设session保存在数据库,每执行一次链接一次数据库,每次都要时时更新的话 # 会非常损耗内存 session["xxx"] = 123 session["xxx2"] = 123 session["xxx3"] = 123 session["xxx4"] = 123 del session["xxx2"] #在这删除了,真正存储的时候是没有xxx2的 return "ddsf" if __name__ == '__main__':
消息闪现
一般一个用户操作完成后,我们要告诉用户操作的结果,用户看完后这个消息就没必要存在了,我们就可以删除它.
Flask的消息闪现系统就是提供了这个功能.也就是反馈
flask()它可以将要闪现的内容存储起来.
get_flashed_messages():它可以把储存的内容取出来显示,然后自动删掉.
本质:闪现是基于session创建的,flash支持往里边放值,只要你取一下就没有了,相当于pop了一下。不仅把值取走,而且吧session里的东西去掉,可以用session直接写
from flask import Flask,flash,get_flashed_messages app = Flask(__name__) app.secret_key = 'asdfasdfasdf' @app.route('/x1',methods=['GET','POST']) def login(): flash('我要上向龙',category='x1') #把内容放入flash中,category就是一个别名 return '视图函数x1' @app.route('/x2',methods=['GET','POST']) def index(): data = get_flashed_messages(category_filter=['x1']) #取数据 print(data) return "视图函数x2" if __name__ == '__main__': app.run()
先执行一次/x1
执行两次/X2
结果
['我要上向龙'] [] #第二次就取不出值来了
用的场景: 阅后即焚
登录场景:
flash.py
rom flask import render_template, request, session, url_for, redirect, flash @app.route('/') def index(): if 'user' in session: return render_template('hello.html', name=session['user']) else: return redirect(url_for('login'), 302) @app.route('/login', methods=['POST', 'GET']) def login(): if request.method == 'POST': session['user'] = request.form['user'] flash('Login successfully!') return redirect(url_for('index')) else: return ''' <form name="login" action="/login" method="post"> Username: <input type="text" name="user" /> </form>
login.html
!doctype html> <title>Hello Sample</title> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}"> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flash"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <div class="page"> {% block body %} {% endblock %} </div>
全局装饰器
@app.before_request和@app.after_request
app.before_request和app.after_request相当于djano中的中间件函数process_request和process_response,服务于全局,
不同点:当before_request有返回值时.它会直接执行所有的after_request,而不是像djano中执行对应的after_request函数
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from flask import Flask,session app=Flask(__name__) app.secret_key="dfsadsfds" @app.route('/login') def login(): session['login_info']=123 return '123' @app.route('/index3') def index(): ret=session.pop('login_info') print('ret',ret) return '这是主页' @app.before_request def before(): print('brefore') @app.after_request def after(response): #注意这里的参数和返回值 print('after') return response if __name__ == '__main__': app.run(debug=True) app.__call__
结果:
brefore
after
还有其他的一些全局装饰器
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.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 def before_first_request1(): print('before_first_request1') @app.before_first_request def before_first_request2(): print('before_first_request2') @app.before_request def before_request1(): Request.nnn = 123 print('before_request1') @app.before_request def before_request2(): print('before_request2') @app.after_request def after_request1(response): print('before_request1', response) return response @app.after_request def after_request2(response): print('before_request2', response) return response @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.template_global() def sb(a1, a2): return a1 + a2 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 @app.route('/') def hello_world(): return render_template('hello.html') if __name__ == '__main__': app.run()
中间件
这个中间件是发生在request形成之前,也就是在执行app.wsgi_app()之前添加一些操作,通过对象的__call__方法
没多大用处,一般我们用@app.before_request
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from flask import Flask from flask import session, request app = Flask(__name__) app.secret_key = 'asdfasdfasdf' @app.route('/x2',methods=['GET','POST']) def index(): print('我是index') return "我是x2" class Middleware(object): def __init__(self,old_wsgi_app): """ 服务端启动时,自动执行 :param old_wsgi_app: """ self.old_wsgi_app =old_wsgi_app def __call__(self, environ, start_response): """ 每次有用户请求道来时 :param args: :param kwargs: :return: """ print('before')#添加在形成request之前的操作 obj = self.old_wsgi_app(environ, start_response) print('after') #添加在形成request之后的操作 return obj if __name__ == '__main__': app.wsgi_app = Middleware(app.wsgi_app) app.run() """ 执行app.run()时候 1.先执行app.__call__, 2.再调用app.wsgi_app(),因为我自己有wsgi_app属性,加括号执行我的__call__方法,然后就把老的app.wsgi_app传进来,这样我们就可以在老的app.wsgi_app执行前,和执行后做一些操作 """
结果:
before
我是index
after
蓝图blueprint
详情见flask组件中的数据库连接池
蓝图是什么?
第一个功能:如果代码很多要进行归类,blueprint就是对flask的目录结构进行划分(应用于小中型程序),使目录结构更清晰,
第二个功能:当一个py文件中有多个函数时,为了使url容易区分可以给url添加前缀.
第三个功能: 我们知道@app.before_rerequest对全局生效,然而蓝图可以让它只对某一个蓝图实例生效
我们为什么要用蓝图?
我们的应用经常会区分用户站点和管理员后台
两者虽然都在同一个应用中,但是风格迥异。把它们分成两个应用吧,总有些代码我们想重用;放在一起嘛,耦合度太高,代码不便于管理。所以Flask提供了蓝图(Blueprint)功能。
蓝图使用起来就像应用当中的子应用一样,可以有自己的模板,静态目录,有自己的视图函数和URL规则,蓝图之间互相不影响。但是它们又属于应用中,可以共享应用的配置。
对于大型应用来说,
我们可以通过添加蓝图来扩展应用功能,而不至于影响原来的程序。不过有一点要注意,目前Flask蓝图的注册是静态的,不支持可插拔。
转载于http://www.bjhee.com/flask-ad6.html
例子
首先这样创建目录
首先,manage.py文件是我们的启动文件
from blue_flask import app if __name__ == '__main__': app.run(debug=True)
admin.py文件中创建一个蓝图实例
from flask import Blueprint ad=Blueprint('ad',__name__,url_prefix='/admin') #实例化一个蓝图对象,给url添加前缀 @ad.route('/ad1') def ad1(): return '这是ad1页面' @ad.route('/ad2') def ad2(): return '这是ad2页面'
acount.py文件中我们再创建一个蓝图实例,并添加一个before_request装饰器
from flask import Blueprint act=Blueprint('act',__name__) #实例化一个蓝图对象,里边的名字随便起,但是不要和url相同 @act.route('/acount') def login(): return '这是acount' @act.before_request def f (): print('我只对act蓝图实例有效')
__init__.py文件是我们的初始化文件,在这里我们导入蓝图,并注册蓝图
from flask import Flask #创建一个app对象 app=Flask(__name__) from blue_flask.views.admin import ad from blue_flask.views.acount import act #给新蓝图注册 app.register_blueprint(ad) app.register_blueprint(act)
这样我们就可可以执行manage.py文件了,
在url中输入/acount,我们可以在前端看到"这是account",在后台打印出"我只对act蓝图实例有效"
当我们在url这样输入/admin/ad1,在前端会显示这是ad1页面,在后台不会打印出f装饰器的内容