一、初识Flask
Flask扩展应用扩展包
二、认识werkzurg,Flask最重要的依赖
Python Web 框架工具包 werkzeug
#?引用werkzurg的功能模块
from?werkzeug.wrappers?import?Request,Response
from?werkzeug.serving?import?run_simple
#?底层的用法
def?run(environ,start_response):
????return?[b'abcdefg']?
if?__name__?==?'__main__':
????run_simple('localhost',4000,run)?#?监听端口并执行run函数
????#?另一种用法
@Response.application
def?hello(request):
????return?Response('Hello?World!')
????'''
????????return到底返回到客户端什么内容?
????????1、状态码:status?code?200,301,404,返回一个响应的客户端的状态码;
????????2、content-type:告诉客户端用什么方式解析返回的值;
????????3、其他的返回头信息。
????'''
if?__name__?==?__main__:
????run_simple('localhost',4000,hello)
三、Flask 使用
3.1、简单的登录
from?flask?import?Flask?as?fk?,?request,render_template?as?render,redirect,session,make_response
app?=?fk(__name__)
app.secret_key?=?'abckd'?#?设置session?的加密值
@app.route('/',methods=['GET','POST'])
def?index():
????if?request.method?==?'POST':
????????user?=?request.form.get('user')
????????pwd?=?request.form['pwd']
????????if?user?==?'admin'?and?pwd?==?'123':
????????????print(user,pwd)
????????????return?'登录成功!'
????return?render('index.html',h1='你好')
????#?home.add_url_rule('/',view_func=index)?#第二种路由的声明方式,必须已“/”开头
if?__name__?==?'__main__':
????app.run(debug=True,host='192.168.0.11',port=5000)?
'''
执行werkzurg中的run_simple('localhost',4000,hello)
这里只是socket进行了请求监听,当浏览器发起请求后,执行内部的__call__()方法。
'''
3.2、静态文件的处理方法
<!--?使用静态文件需要删除?<!DOCTYPE?html>?-->
<html>
<head>
????<meta?charset="UTF-8">
????<title>Title</title>
????<link?rel="stylesheet"?href="/static/index.css"?type="text/css">
????<!--?javascript引用时必须添加type="text/javascript"?-->
????<script?language="javaacript"?src="/statics/jquery.min.js"?type="text/javascript"></script>?
????<!--?推荐使用这样加载静态文件?-->
????<link?rel="stylesheet"?href="{{?url_for('static',filename='index.css')?}}"?type="text/css">?
????<!--?url_for(),函数配置静态文件,一定要在Flask类中设置静态文件的路径和别名,蓝图中设置静态文件路径和别名是
????不能使用的?-->
</head>
<body>
<h1>index</h1>
<h1>{{?h1?}}</h1>
<form?action="/"?method="POST">
????<input?type="text"?name="user"?>
????<input?type="password"?name="pwd">
????<input?type="submit"?value="提交">
</form>
</body>
使用 url_for 函数获取静态资源时,必须在实例化 Flask 对象的时候设置 static_url_path(别名),static_folder(静态文件路径)。
from?flask?import?Flask from?.views.index?import?home def?shb(): ????app?=?Flask(__name__,static_url_path='/static',static_folder='./public',template_folder='./template') ????''' ????PS: ?????1、static_folder的路径一定要设置正确。 ?????2、static_url_path的值必须是’/static‘,其他测试无效。 ?????3、url(’static‘,filename='静态文件路径')?第一个值必须是’static‘,其他测试无效。 ?????4、template_folder?配置注意存放路径。 ?????5、蓝图中就不再配置存放静态文件的路径和引用别名了;模板也不用再配置,这里是全局配置。 ????''' ????app.register_blueprint(home,url_prefix="/api") ????return?app
3.3、Flask 简单运用
from?flask?import?Flask,render_template,request,redirect,session
app?=?Flask(__name__)?#?__name__?可以修改为任意字符串,并可以传入多个参数
app.secret_key?=?'abckd'?#?设置session加密多余字符串
#?路由装饰器,必须已“/”开头
@app.route('/login',methods=['GET','POST'])
#?定义与路由装饰器匹配的执行函数
def?login():
????print(request.method)?#?request?需要导入,获取请求的信息
????session['key]?=?value?#?设置session值
????session.get('key')?#?获取session的值
????return?render_template('login.html',**{key:value})
????#return?render_template('login.html',key=value)
if?__name__?==?'__main__':
????app.run(url,prot)
3.4、Flask 的实现基础
Python Threading 线程模块用法
3.4.1、Threading.local 多线程
作用:为每个线程创建一个独立的空间,使得线程对自己空间中的数据进行操作(数据隔离)。
import?threading from?threading?import?local local_obj?=?local()?#?实例化local对象 def?task(i): ????local_obj.xxx?=?i?#?为local对象设置一个参数 ????print(local_obj.xxx,i)?#?获取local对象中的参数 ????#?threading.get_ident()?获取线程的唯一标示 ????print(threading.get_ident(),i) for?i?in?range(10): ????t?=?threading.Thread(target=task,args(i,))?#?创建线程 ????t.start()?#?开始执行线程
3.4.2、根据字典自定义类似Threading.localz
import?threading
import?greenlet?#?获取协程信息
DIC?=?{}
def?task(i):
#?获取协成的唯一标记
#?indent?=?greenlet.getcurrent()
#?treading.get_ident()?获取线程的唯一标记
????indent?=?treading.get_ident()
????if?indent?in?DIC:
????????DIC[indent]['xxx']?=?i
????else:
????????DIC[indent]?=?{'xxx':i}
????print(DIC[index][xxx],i)?#?打印字典中的参数
for?i?in?range(10):
????t?=?threading.Thread(target=task,args=(i,))?#?创建线程
????t.start()?#?开始执行线程
3.4.3、自定义升级版Threading.localz
此版本中协程与线程可以完全的兼容,完成对请求的数据隔离。 为什么需要给线程和协程数据进行隔离? 在多个请求的时候,由于每个请求的信息不相同,所以需要对不同的请求信息进行分类管理(数据隔离),从而防止数据混乱; 这样在需要使用视图函数取得用户请求信息的时候,才能根据不同的信息进行取值和修改。
import?threading
import?time
?
try:
????import?greenlet?#?获取协程信息
????get_indent?=?greenlet.getcurrent
except?Exception?as?e:
????get_indent?=?threading.get_ident
class?local(object):
#?DIC={}
????def?__init__(self):
???#?pass
????????object.__setattr__(self,'DIC',{})?#?通过父类的方法设置类属性
????def?__getattr__(self,item):
????????indent?=?get_indent()
????????if?indent?in?self.DIC:
????????????return?self.DIC[indent].get(item)
????????else:
????????????return?None
????
????def?__setattr__(self,key,value):
????????indent?=?get_indent()
????????if?indent?in?self.DIC:
????????????self.DIC[indent][key]?=?value
????????else:
????????????self.DIC[indent]=?{key:value}
????
????obj?=?local()?#?类在实例化的时候运行__init__()方法
??'''
????obj.xx?#?对象.方法的时候运行__getattr__()方法,并且把xx当参数传入
????obj.xx?=?123?#?对象.方法赋值的时候运行__setattr__()方法,并且把xx和123当参数传入
??'''
????def?task(i):
????????obj.xxx?=?i
????????time.sleep(2)
????????print(obj.xxx,i)
????for?i?in?range(10):
????????t?=?threading.Thread(target=task,args=(i,))?#?创建线程
????????t.start()?#?开始执行线程
3.4.4、Flask简单执行流程
3.4.4.1、Flask 的基本执行流程
封装?requestContext?对象,?full_dispatch_request(视图函数?执行),?response返回 从app.run()?开始?-->> Flask的__call__方法-->> wsgi_app?(封装RequestContext(request,session)对象到?localstack)?-->> full_dispatch_request(视图函数?执行)?-->> 执行扩展(before_request)?,触发信号?-->> 获取response?-->> pop?reqeust、session?--?>> 结束
3.4.4.2、Flask 源码执行顺序
(1)threading?local?和?flask的自定义local对象?
????????-?基于本地线程?可以实现线程隔离。
(2)请求到来
????????封装??ctx?=?RequestContext(request,session)
?????????????ctx?--?放入?Local?__storage__?{?'id':{stack:[ctx]}?}
(3)执行视图
????????导入?request
????????print(reqeust)?--?>>?localproxy?__str__
????????reqeust.method?--?>>?localproxy?__getattr__
????????reqeust?+?1?--?>>?localproxy?__add__????
????????调用_lookup_req_object函数,去local中的ctx中获取reqeust?session
(4)请求结束
????????ctx.auto_pop
????????ctx?从?local?中移除
四、Flask中的方法
#?Flask?中的模块
from?flask?import?Flask,render_template,request,redirect,session
request.method?#?获取请求的方法
request.args?#?获取get请求的数据
request.args.get('')?#?获取get请求指定的值
request.form?#?获取post请求的数据
request.form.get('')?#?获取post请求指定的值
files?=?request.files.get('')?#?获取POST上传的文件信息
files.filename?#?获取上传的文件名
files.stream?#?获取上传文件的内容
files.save('文件路径','上传文件的内容',)?#?保存上传文件到本地
'''
session?的处理?flask?放入的是加密cookie中的,继承了字典的所有功能;
flask读取cookie中session对应的值,将该值解密并反序列化成为字典,供视图函数使用;
当请求结束时,flask会读取内存中字典的值,进行序列化并加密,写入到浏览器cookie中。
'''
session['key']?=?value?#?为session赋值?保存在浏览器的cookie中
session.get('')?#?获取session的值
del?session['key']?#?删除
#?设置响应头
rst?=?make_response('aaa')?#?设置返回加密
rst.headers['Access-Control-Allow-Origin']?=?'*'?#?允许请求的域名
rst.headers['Access-Control-Allow-Methods']?=?'POST'?#?允许POST请求
#?Flask()?配置项?
import_name,?#?文件名称
static_url_path=None,?#?静态文件别名
static_folder='static',?#?静态文件路径
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder='templates',?#?模板文件路径
instance_path=None,?#?实例文件路径
instance_relative_config=False,?#?实例配置文件相对路径
root_path=None?#?根文件路径
#?Flask?配置文件?app.config['ENV']?获取内部的值
'ENV':????????????????????????None,
'DEBUG':???????????????????????None,
'TESTING':?????????????????????False,
'PROPAGATE_EXCEPTIONS':?????????????None,
'PRESERVE_CONTEXT_ON_EXCEPTION':???????None,
'SECRET_KEY':???????????????????None,
'PERMANENT_SESSION_LIFETIME':?????????timedelta(days=31),?#?session保留时间
'USE_X_SENDFILE':?????????????????False,
'SERVER_NAME':???????????????????None,?#?设置域名?xxx测试数据
'APPLICATION_ROOT':???????????????'/',
'SESSION_COOKIE_NAME':?????????????'session',
'SESSION_COOKIE_DOMAIN':????????????None,
'SESSION_COOKIE_PATH':?????????????None,
'SESSION_COOKIE_HTTPONLY':???????????True,
'SESSION_COOKIE_SECURE':????????????False,
'SESSION_COOKIE_SAMESITE':???????????None,
'SESSION_REFRESH_EACH_REQUEST':???????True,?#?session以什么方式保存保留时间
'MAX_CONTENT_LENGTH':??????????????None,?#?上传文件大小限制
'SEND_FILE_MAX_AGE_DEFAULT':?????????timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS':???????????None,
'TRAP_HTTP_EXCEPTIONS':?????????????False,
'EXPLAIN_TEMPLATE_LOADING':??????????False,
'PREFERRED_URL_SCHEME':?????????????'http',
'JSON_AS_ASCII':?????????????????True,
'JSON_SORT_KEYS':?????????????????True,
'JSONIFY_PRETTYPRINT_REGULAR':????????False,
'JSONIFY_MIMETYPE':???????????????'application/json',
'TEMPLATES_AUTO_RELOAD':????????????None,
'MAX_COOKIE_SIZE':?4093,
'SQLALCHEMY_DATABASE_URI'?:????????????????'mysql://root:root@127.0.0.1/mysql',?#?配置数据库连接
'SQLALCHEMY_TRACK_MODIFICATIONS'?:?????????False,
#?自定义配置
'SERVER_PORT':?????????????????????????????8000,?#?端口
'IP_PATH':?????????????????????????????????'127.0.0.1',#?访问链接
#?绑定二级域名的方法 app.url_map.default_subdomain?=?'www'?#?设置域名前缀 app.config['SERVER_NAME']?=?'testing测试数据'?#?设置域名 app.register_blueprint(public,?subdomain='static')?#?设置静态文件 PS:要指定默认的域名(app.url_map.default_subdomain?),不然无法访问; ????SERVER_NAME、app.url_map.default_subdomain?在开发环境是不能加,会报404。
五、Flask配置文件
5.1、通过字符串获取 Class 并运行(反射)
hasattr、setattr、getattr 解释
hasattr(object, name) 判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
class?test(): ????name="xiaohua" ????def?run(self): ????????return?"HelloWord" t=test() hasattr(t,?"name")?#判断对象有name属性 #输出:True hasattr(t,?"run")??#判断对象有run方法 #输出:True
setattr(object, name, values) 给对象的属性赋值,若属性不存在,先创建再赋值。
getattr(object, name[,default]) 可以取出来某个属性,这个属性如果是一个字段,就得到字段的值了,如果是一个方法,就得到这个方法的指针了,然后可以根据方法的指针来调用方法。
class?test(): ????name="xiaohua" ????def?run(self): ????????return?"HelloWord" t=test() setattr(t,'max',30)?#在对象中设置max属性 getattr(t,?"name")?#获取name属性,存在就打印出来。 #输出:'xiaohua' getattr(t,?"run")??#获取run方法,存在就打印出方法的内存地址。 #输出:<bound?method?test.run?of?<__main__.test?instance?at?0x0269C878>> getattr(t,?"run")()??#获取run方法,后面加括号可以将这个方法运行。 #输出:'HelloWord' getattr(t,?"age")??#获取一个不存在的属性。 ''' Traceback?(most?recent?call?last): File?"<stdin>",?line?1,?in?<module> AttributeError:?test?instance?has?no?attribute?'age' ''' getattr(t,?"age","18")??#若属性不存在,返回一个默认值。 #输出:'18'
5.2、Flask配置文件的原理
#?settings.py?文件 class?Foo(object): ????DEBUG?=?True
#?获取settings.py中类的项目
import?importlib
path?=?'settings.Foo'
p,c?=?path.rsplit('.',maxsplit=1)
m?=?importlib.import_module(p)
cls?=?getattr(m,c)
#?如果找到这个类
for?key?in?dir(cls):
????if?key.isupper():
????????print(key,getattr(cls,key))
#?输出
#?DEBUG?True
5.3、Flask 三种加载配置方式
在 Flask 程序运行的时候,可以给 Flask 设置相关配置,比如:配置 Debug 模式,配置数据库连接地址等等,设置 Flask 配置有以下三种方式:
从配置对象中加载(常用):app.config.from_object()
从配置文件中加载:app.config.from_pyfile()
从环境变量中加载(了解):app.config.from_envvar()
以下演练以设置应用程序的 DEBUG(调试模式) 为例,设置应用为调式模式这后,可以实现以下功能。
5.3.1、配置文件
创建配置文件 config.py,在配置文件中添加配置。
app.config.from_pyfile('config.py')
5.3.2、环境变量(了解)
加载指定环境变量名称所对应的相关配置
app.config.from_envvar('FLASKCONFIG')
#?读取配置
app.config.get()
在视图函数中使用 current_app.config.get() 注:Flask 应用程序将一些常用的配置设置成了应用程序对象的属性,也可以通过属性直接设置/获取某些配置:app.debug = True
5.3.3、Flask配置文件的原理
#?settings.py?文件 class?Foo(object): ????DEBUG?=?True
#?获取settings.py中类的项目
import?importlib
path?=?'settings.Foo'
p,c?=?path.rsplit('.',maxsplit=1)
m?=?importlib.import_module(p)
cls?=?getattr(m,c)
#?如果找到这个类
for?key?in?dir(cls):
????if?key.isupper():
????????print(key,getattr(cls,key))
#?输出
#?DEBUG?True
5.4、配置对象加载配置
''' ?app.config?#?获取配置文件对象 ?app.config['ENV'],app.config['ENV']?=?‘aaa'??#?获取和设置配置文件的值 ?app.config.from_object(‘settings.Foo’)??#?通过外部文件引入配置文件 ''' ?#?settings.py?文件 ?class?类名: ?配置文件key?=?配置文件value ?#?使用?settings.py?文件,文件名任意。 ?app.config.from_object(‘settings.类名’)
#?主程序运行文件
from?flask?import?Flask
from?.views.account?import??bpaccount?#?导入视图
from?.views.code?import?bpcode
app?=?Flask(__name__)
app.config.from_object("settings.TestingConfig")?#导入配置文件配置类
#?读取配置文件?app.config['DEBUG']
def?create_app():
????app=Flask(__name__)
????app.config.from_object("settings.DevelopmentConfig")#导入配置文件配置类
????app.register_blueprint(bpaccount)
????app.register_blueprint(bpcode)
????return?app
#?settings文件 class?Config(object):?#?公共的配置类 ????DEBUG?=False ????SECRET_KEY="ADAFA" ???? #?不同应用环境的不同配置类 class?ProductionConfig(Config): ????DEBUG?=?False class?DevelopmentConfig(Config): ????DEBUG?=?False class?TestingConfig(Config): ????TESTING=True
六、路由系统
6.1、endpoint 的使用
from?flask?import?Flask,?url_for
app?=?Flask(__name__)
?#?endpoint?根据名字反向生成URL?'/index'?。如果不设置默认为函数名?url_for(?'index')
@app.route('/home',methods=['GET'],endpoint='n1')
def?index():
????print(url_for('n1',name=124))?#?反向生成的URL
????return?'Index'
#?app.add_url_rule('/home',view_func=index,methods=['GET'],endpoint='n1')?#?第二种定义路由的方法
if?__name__?==?'__main__':
????app.run(debug=True)?#?修改自动重启Flask
'''
?url_for('n1',name=124)
?输出:/home?name=124
'''
6.2、路由传参
?'''
??动态路由:
???/index/<int:nid>?只接收整数
???/index/<float:nid>?接收浮点数
???/index/<name>?接收字符串,自定义
???/index/默认为字符串?
?'''
from?flask?import?Flask,request,render_template?as?render,redirect,session,make_response?as?response,url_for
app?=?Flask(__name__,static_url_path='/static/')
@app.route('/<int:nid>',methods=['GET','POST'])
#?@app.route('/<aname>/<int:aid>',methods=['GET','POST'])
def?index(nid):
????print(url_for('index',nid=777))
?#?print(url_for('index.n1',aname=aname,aid=aid))
????if?request.method?==?'POST':
????????user?=?request.form.get('user')
????????pwd?=?request.form.get('pwd')
????if?user?==?'admin'?and?pwd?==?'123':
????????return?render('index.html',success='提交成功!')
????????return?render('index.html',h1='首页')
????if?__name__?==?'__main__':
????????app.run()
?'''
?输出:/index/777
?'''
6.3、 route() 参数
rule?#?URL规则
view_func?#?视图函数名称
methods=['GET']?#?请求的方式
endpoint=None?#?名称用于反向生成URL,即:url_for('名称')
strict_slashes=None?#?对URL最后的?‘/’?符号是否严格要求?True:严格要求?False:不严格要求
redirct_to='/new/<int:nid>'?#?重定向
?
????app.config['SERVER_NAME']?=?‘主域名’
????subdomain='url'?#?配置主域名下子域名访问地址,写入'<username>'表示任意子域名
6.4、自定义正则
from?flask?import?Flask,render_template?as?render,url_for
from?werkzeug.routing?import?BaseConverter
app?=?Flask(__name__,static_url_path='/static',static_folder='static')
#?自定义正则转换器
class?RegexConverter(BaseConverter):
????def?__init__(self,?map,?*args):#?map?路由传入的路径集合
????????super(RegexConverter,?self).__init__(map)
????????#?将接受的第1个参数当作匹配规则进行保存
????????self.regex?=?args[0]
????def?to_python(self,?value):#?正则匹配URL时触发
????????return?int(value)?#?value为匹配到的值
????def?to_url(self,?value):?#?方向生成URL触发
????????val?=?super(RegexConverter,?self).to_url(value)
????????return?val?#?url_for()?nid?的值
#?将自定义转换器添加到转换器字典中,并指定转换器使用时名字为:?reg
app.url_map.converters['reg']?=?RegexConverter
@app.route('/<reg("\d+"):nid>',methods=['GET','POST'])
def?index(nid):
????print(url_for('index',nid=222))
????return?render('index.html',h1='index')
if?__name__?==?'__main__':
????app.run()
6.5、浏览器请求URL,Flask视图函数访问流程
6.5.1、循环引用的执行流程
6.5.2、找不到视图函数的解释
为什么循环引用后,无法找到视图函数?
在上图中执行流程中执行了两次 app = Flask(__name__),第一次在fisher主执行流程时,app被赋值一次,但是在book引用fisher主执行文件以后再次执行了 app = Flask(__name__),这样fisher主执行流程的app被后执行的book引用app所覆盖,所以当book引用中流程执行到 if 跳到主执行流程继续执行时, app 已经是 book 模块的赋值,因此就会找不到视图函数。
可以通过 id() 打印app的内存地址来查看两个app是否是同一个app。
七、FBV 视图
FBV 视图:利用函数与路由建立关联,实现请求响应的控制。 CBV 视图:通过定义类的方式来建立视图与路由的关联。
#?CBV视图
from?flask?import?Flask,views
app?=?Flask(__name__,static_url_path='/static',static_folder='static')
class?UserViews(views.MethodView):
????methods?=?['GET']?#?设置请求方式
????decorators?=?[]?#?设置装饰器(全部)?
????def?get(self,*args,**kwargs):
????????return?'GET'
????def?POST(self,*args,**kwargs):
????????return?'POST'
app.add_url_rule('/index',None,UserViews.as_view('uuu'))
if?__name__?==?'__main__':
????app.run()
7.1、请求相关request
下面是request可使用的属性,其中'*'是比较常用的。
*form?
一个从POST和PUT请求解析的?MultiDict(一键多值字典)。
*args?
MultiDict,要操作?URL?(如??key=value?)中提交的参数可以使用?args?属性:
searchword?=?request.args.get('key',?'')
*values?
CombinedMultiDict,内容是form和args。?
可以使用values替代form和args。
*cookies?
顾名思义,请求的cookies,类型是dict。
stream?
在可知的mimetype下,如果进来的表单数据无法解码,会没有任何改动的保存到这个·stream·以供使用。
很多时候,当请求的数据转换为string时,使用data是最好的方式。这个stream只返回数据一次。
*headers?
请求头,字典类型。
*data?
包含了请求的数据,并转换为字符串,除非是一个Flask无法处理的mimetype。
*files?
MultiDict,带有通过POST或PUT请求上传的文件。
environ?
WSGI隐含的环境配置。
*method?
请求方法,比如POST、GET。
path
.script_root
.url
.base_url
.url_root?
如果用户请求如下URL:?
http://HdhCmsTestexample测试数据/myapplication/page.html?x=y
以上的参数内容如下:
is_xhr?
如果请求是一个来自JavaScript?XMLHttpRequest的触发,则返回True,这个只工作在支持
X-Requested-With头的库并且设置了XMLHttpRequest。
blueprint?
蓝本名字。
endpoint?
endpoint匹配请求,这个与view_args相结合,可是用于重构相同或修改URL。当匹配的时候发生异常,会返回None。
get_json(force=False,?silent=False,?cache=True)
json?
如果mimetype是application/json,这个参数将会解析JSON数据,如果不是则返回None。?
可以使用这个替代get_json()方法。
max_content_length?
只读,返回MAX_CONTENT_LENGTH的配置键。
module
如果请求是发送到一个实际的模块,则该参数返回当前模块的名称。这是弃用的功能,使用blueprints替代。
on_json_loading_failed(e)
routing_exception?=?None?
如果匹配URL失败,这个异常将会/已经抛出作为请求处理的一部分。这通常用于NotFound异常或类似的情况。
url_rule?=?None?
内部规则匹配请求的URL。这可用于在URL之前/之后检查方法是否允许(request.url_rule.methods)?等等。?
默认情况下,在处理请求函数中写下?print('request.url_rule.methods',?request.url_rule.methods)?
会打印:
request.url_rule.methods?{‘GET’,?‘OPTIONS’,?‘HEAD’}
view_args?=?None?
一个匹配请求的view参数的字典,当匹配的时候发生异常,会返回None。
7.2、响应相关
from?flask?import?Flask,make_response?as?response,jsonify,render_template?as?render,redirect
'''
????make_response?封装所有形式的返回响应体,并可以设置返回响应头
????res?=?response(render('index.html',**{'k1':'v1'}))
????return?res
????返回响应体
????return?''?返回字符串
????return?jsonify({'k1':'v1'})?返回json字符串
????return?render('index.html',**{'k1':'v1'})?返回一个文件
????return?redirect('http://HdhCmsTestbaidu测试数据')?返回重定向URL
'''
app?=?Flask(__name__,static_url_path='/static/')
app.secret_key?=?'abck'
@app.route('/',methods=['GET','POST'])
def?index():
????res?=?response('index.html')??#?封装了返回字符串响应体
????res.headers['xxx']?=?123?#?设置返回响应头
????res.set_cookie?=?321?#?设置cookies
????return?res?#?返回请求体,请求头,
if?__name__?==?'__main__':
????app.run()
7.3、before_request 装饰器
from?flask?import?Flask,make_response?as?response,jsonify,render_template?as?render,redirect,request
app?=?Flask(__name__,static_url_path='/static/')
#?app.before_request后面的函数,在所有函数执行前执行。
#?app级中app.before_request(函数名)直接使用。
#?蓝图中使用装饰器的方式
@app.before_request?
def?func():
?if?request.path?==?'/index':
???return?None
?return?'返回值'
??#?返回值不是None,不再往后执行,直接返回return的值
??#?返回None,才往后执行其他,这里执行index函数
?
@app.route('/',methods=['GET','POST'])
def?index():
?res?=?response('index.html')??#?封装了返回字符串响应体
?res.headers['xxx']?=?123?#?设置返回响应头
?res.set_cookie?=?321?#?设置cookies
?return?res?#?返回请求体,请求头,
?
if?__name__?==?'__main__':
?app.run()
八、模板渲染
8.1、基本语句的使用
#?字符串,字典,列表等基本数据类型,都可以通过python本来的方法操作。
{{?属性名.0?}}
{{?属性名[0]?}}
{{?属性名()?}}
?
#?html标签转换
{{?html标签|safe?}}
from?flask?import?Markup
Markup('<h1>h1标签</h1>')
#?设置默认
{{?属性名|default('你好')?}}
8.2、设置全局模板函数
#?设置全局所用的函数
@app.template_global()
def?sb(a1,a2):
????return?a1?+?a2
#?使用:{{?sb(a1,a2)?}}
?
#?过滤filter?的使用
@app.template_filter()
def?sb(a1,a2,a3):
????return?a1?+?a2?+?a3
#?使用:{{?a1|sb(a2,a3)?}}
8.3、模板的继承
模块:{%?block?content?%}{%?endblock?%}
模板文件:
?<html>
<head>
????<meta?charset="UTF-8">
????<title>首页</title>
????<link?rel="stylesheet"?href="{{?url_for('static',filename='index.css')?}}"?type="text/css">
</head>
<body>
????<div>
????????{%?block?content?%}{%?endblock?%}
????????<a?href="{{?url_for('admin.index',id=1234)}}">url_for跳转带参数</a>
????</div>
</body>
?</html>
----------------------------------------------------------------------------------------
?<!--?导入模板文件?-->
?{%?extends?'./template.html'?%}
?{%?block?content?%}
??<h1>?你好?</h1>
????{{?h1?}}
?{%?endblock?%}
调用模块:
需要使用模块文件的引入:?
{%?extents?'模板文件'?%}?
模块代码:
{%?block?content?%}
<h1>?你好?</h1>
{{?h1?}}
{%?endblock?%}
-------------------------------------------------------------------------------------
导入模板:{%?include?'模板文件'?%}
?
宏定义:{%?macro?ccc(name,type='text',value='')?%}?#?相当于定义def?函数
<input?type="{{?type?}}"?name="{{?name?}}"/>
<input?type="submit"?value="提交"/>
{%?endmacro?%}
使用宏:{{?ccc('n1')?}}
8.4、url_for生成静态文件URL连接
'''
#?常规构造静态文件链接?
1.包含可变部分的动态路由,url_for(endpoint,_external=True,**kwargs)以视图函数端点名和可选参数作为参数,
??当_external为True时返回url绝对路径,否则返回相对路径
2.url_for(endpoint,_external=True,**kwargs)中可以为动态静态文件部分传递参数,
??如url_for('.static',_external=True,filename='js/html5shiv.min.js'),
??返回的url是http://localhost/static/js/html5shiv.min.js
'''
#?不在蓝图中使用?url_for?构建静态文件路径?
#?http://localhost/static/js/fixes/html5shiv.min.js?
print?url_for('static',?filename='js/fixes/html5shiv.min.js',?_external=True)
'''
PS:蓝图中endpoint必须加上当前命名空间前缀表示当前命名空间,如定义的main蓝图,必须如下使用
url_for('main.static',_external=True,filename='js/html5shiv.min.js')
'''
#?蓝图中使用url_for构建反向连接时必须添加蓝图名
#?http://localhost/static/js/fixes/html5shiv.min.js?
print?url_for('.static',?filename='js/fixes/html5shiv.min.js',?_external=True)
print?url_for('main.static',?filename='js/fixes/html5shiv.min.js',?_external=True)
8.5、错误页面设置
定义错误页面用装饰器 @app.errorhandler() 来完成,在定义的过程中必须遵循的是在 Flask 直接实例化下定义,不然会定义不成功。
#?_*_?coding:utf-8?_*_
from?flask?import?Flask,?render_template?as?render
app?=?Flask(__name__)
@app.errorhandler(404)
def?page_not_found(error):
????return?render('/home/404.html'),?404
九、Flash和特殊装饰器
9.1、flash 消息闪现
在使用 flash() 时会使用到session,所以需要在配置文件中配置 SECRET_KEY。
from?flask?import?flash,get_flashed_messages
flash('',category='数据分类属性')?#?设置
flash('Error',category='error')?
get_flash_messages()?#?获取
get_flash_messages(category_filter=['数据分类属性'])?#?通过分类获取
'''
flash?实现原理:
????flash基于session来处理内容,先将flash('')中的值存入session中,再通过session的pop移除,达到用一次就消失的效果。
'''
#?session自己实现
session.pop('属性名')
HTML中获取 flash() 信息
在模板引擎中怎样定义一个变量;通过关键字 set 定义一个变量。category_filte 过滤返回的类别。
{%?set?messages?=?get_flash_messages(category_filter=["error"])?%}
9.2、Flask中间件
9.2.1、特殊装饰器
#设置全局模板函数
@template_global()
@template_filter()
#?app.route视图函数执行前执行,只运行第一次
@app.before_first_request
def?x1():
????print('before_first')
#?app.route视图函数执行前执行,多个谁先定义谁先执行
@app.before_requesr
def?x2():
????print('before')
#?app.route视图函数执行后执行,必须有参数和返回值,多个谁后定义谁先执行
@app.after_request
def?x3(response):
????print('after')
return?response
#定义404
@app.errorhandler(404)?
def?not_fourd(arg):
????return?'404'
9.2.2、中间件
?'''
??Flask的中间件需要通过自定义方法,去替换源码中的相关方法,利用在程序获得请求时才运行__call__()的特性,
??来创建中间件,从而完成中间件的功能。
?'''
class?Middleware(object):
?def?__init__(self,?old):
?????self.old?=?old
?def?__call__(self,?*args,?**kwargs):
?????print('前')
?????ret?=?self.old(*args,?**kwargs)
?????print('后')
?????return?ret
???
if?__name__?==?'__main__':
????app.wsgi_app?=?Middleware(app.wsgi_app)
十、蓝图
为开发者提供一个目录结构,让开发更规范,可以将特殊装饰器加到蓝图中,并根据不同的返回内容设置不同的域名前缀。
为什么flask的根目录不是第一个flask03,而是第二个flask03?
因为在对 Flask(__name__) 进行实例化时,__name__ 所获取的值决定了Flask根目录的位置。
10.1、蓝图的基本模型
10.1.1、主路由视图函数
__init__.py
from?flask?import?Flask #?导入定义的蓝图模块 from?.views.index?import?n def?app_view(): ????app?=?Flask(__name__,) ????#?注册,url_prefix指定打开URL的访问集?http://127.0.0.1/app/1?http://127.0.0.1/app/2? ????app.register_blueprint(n,url_prefix='/app')#?添加了url_prefix?模板文件静态文件也需要添加'/app' ????return?app
app.py
from?flask03?import?app_view app?=?app_view() print(app) if?__name__?==?'__main__': ????app.run()
10.1.2、分路由视图函数
index.py
#coding:utf8
from?flask?import?Blueprint
#?ps:蓝图名不能与视图函数名一致
n?=?Blueprint('index',__name__,template_folder='xxx',
static_folder='statics',static_url_path='/statics')
'''
?在蓝图中,必须带上“蓝图函数.endpoint名”
?url_for('index.n1',name=name)
?url_for('.n1',name=name)
'''
@n.route('/home/<name>',methods=['GET'],endpoint='n1')
def?index(name):
????print(name)
????print(url_for('index.n1',name=name))
????return?'home'
'''
?访问域名:http://127.0.0.1:5000/api/home/123
?输出:“name”:123
????“url_for('index.n1',name=name)”:/api/home/123
'''
10.2、大项目蓝图目录结构
十一、Flask上下文管理
11.1、Flask执行顺序
上下文管理的内容:
request、session、app、g
1、客户端请求到来时,会将request/session封装到一个对象?RequestContext(self,environ)中(其中self为app对象,
environ为客户端请求的原始数据);
ctx=RequestContext(self,environ)?封装了?request/session?
2、将含有request/session的对象打包,根据线程/协程的唯一标识,作为字典存放在一个
“空间中”(线程/协程唯一标识作为Key,RequestContext对象作为Value),
{
线程/协程唯一标识:{ctx:RequestContext对象}
....
}
3、视图函数运行过程
from?flask?import?request,session
request.method
当有请求过来时,通过线程/协程唯一标识找到,存放在空间中的RequestContext对象,再通过对象找到封装其中的
request/session,最后找到属性.method。
4、请求结束
session保存到浏览器cookie中,并移除字典中的数据,从而结束请求。
11.2、偏函数
import?functools def?index(a1,a2): ????return?a1+a2 #?函数常规的用法 a?=?index(12,20) print(a) ''' ?利用?functools?模块实现偏函数; ?偏函数只要传入一个值,其他的值自动传入 ''' new_func?=?functools.partial(index,666) a1?=?new_func(34) print(a1)
11.3、上下文:执行流程
request 请求执行流程
11.4、上下文sessio
十二、Flask 中的 localProxy 方法
local:维护保存在__storage__字典中的线程/进程;
localStack:视图系统通过其操作local维护的__storage__字典;
localProxy:它的作用主要是帮助 request 获取当前应用的HTTP请求信息,伴 request 运行和消失的。
class?LocalProxy(object): ????def?__init__(self): ????????pass ????def?__getattr__(self): ????????pass ????def?__setattr__(self): ????????pass ????def?__getitem__(self): ????????pass lp?=?LocalProxy()
十三、Flask g变量
1、g作为flask程序全局的一个临时变量,充当着中间媒介的作用,我们可以通过它传递一些数据;
2、g保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别;
3、g对象在一次请求中的所有代码任何地方,都是可以使用的。
g.name='abc'
g变量简单应用
1、创建一个utils.py文件,用于测试除主文件以外的g对象的使用
#?utils.py #encoding:?utf-8 from?flask?import?g def?login_log(): ????print?u'当前登录用户是:%s'?%?g.username def?login_ip(): ????print?u'当前登录用户的IP是:%s'?%?g.ip
2、在主文件中调用utils.py中的函数
#encoding:?utf-8
from?flask?import?Flask,g,request,render_template
from?utils?import?login_log,login_ip
app?=?Flask(__name__)
?
@app.route('/')
def?hello_world():
????return?'Hello?World!'
????
@app.route('/login/',methods=['GET',?'POST'])
def?login():
if?request.method?==?'GET':
return?render_template('login.html')
else:
username?=?request.form.get('username')
password?=?request.form.get('password')
g.username?=?username
g.ip?=?password
login_log()
login_ip()
return?u'恭喜登录成功!'
?
if?__name__?==?'__main__':
????app.run()