Flask基础
发布时间:2020-12-20 10:50:47 所属栏目:Python 来源:网络整理
导读:目录 Flask基础 内容详细 配置文件 路由系统 视图 请求相关的数据 响应 示例程序一 html页面 安全登录版本一 知识点 装饰器适用--版本二 版本三****实现权限管理 模板的渲染 模板后端的书写 session 闪现 中间件 特殊的装饰器**** Flask基础 对于一个web框架
目录
Flask基础对于一个web框架而言,一定要有的就是路由,视图,模板语法 对于一个没有见到的框架,从路由入口,然后走视图 1.谈谈你对django和flask的认识? 1.django是一个大而全的框架,内置了很多的组件,比如分页,缓存,orm... 2.flask轻量级的,可扩展性强,可定制性强 3.两个框架你选择哪个?因人而异 2.flask和django最大的不同点: 1.request/session(django中的session是依附在request对象中传递进来的,但是flask是导入的) 3.flask知识点 - 可以设置静态文件和模板(实例化flask对象的时候配置的...) - 路由装饰器,对象点route @app.route('/index',methods=['GET','POST']) - 请求相关的 request.form request.args request.method - 响应 render redirect -session # 放值 session['xx'] = 123 # 取值 session.get('xx') 内容详细知识点: 给你一个路径,如'xx.xx.ClassFoo',找到这个类获取里面的静态字段 import importlib # 如何找到这个类 path = 'settings.Foo' # 先拿到这个路径,然后利用importlib p,c = path.rsplit('.',maxsplit=1) m = importlib.import_module(p) cls = getattr(m,c) # 路径, 类名 # print(cls) print(dir(cls)) # 拿到的是这个类所有的方法 for k in dir(cls): if k.isupper(): print(k) # DEBUG v = getattr(cls,k) # 第一个参数是你要对谁操作,第二个是你要拿谁的值 print(v) # True 配置文件print(app.config) # 配置文件的查看 # 修改配置文件的方法一 app.config['DEBUG'] = True app.config['DEBUG'] = True app.config['DEBUG'] = True app.config['DEBUG'] = True # 修改配置文件的方法二 app.config.from_object('settings.Foo') # 去看settings中的Foo这个类 class Foo: DEBUG = True # 方法二修改配置文件的源码 def from_object(self,obj): if isinstance(obj,string_types): # obj就是'settings.Foo' obj = import_string(obj) # 根据字符串的形式去导入他 # obj 就是那个传进来的类,在这里就是我们的那个Foo for key in dir(obj): if key.isupper(): # self就是配置文件对象,封装了所有的配置文件 self[key] = getattr(obj,key) # 在这一步进行了修改 def import_string(import_name,silent=False): # import_name='settings.Foo' import_name = str(import_name).replace(":",".") try: try: __import__(import_name) except ImportError: if "." not in import_name: raise else: return sys.modules[import_name] module_name,obj_name = import_name.rsplit(".",1) # 获取路径和类 module = __import__(module_name,globals(),locals(),[obj_name]) # __import__类似于importlib.import_moudle(module) try: # 在这一步将获取到的类传了出去,拿到的是路径和类名 return getattr(module,obj_name) except AttributeError as e: raise ImportError(e) except ImportError as e: if not silent: reraise( ImportStringError,ImportStringError(import_name,e),sys.exc_info()[2] ) # 配置分为开发环境和线上环境 class Base(object): xxx = '123' class Pro(Base): # 线上环境 DEBUG = False class Dev(Base): # 开发环境 DEBUG = True # 后面我们上线了,只需要更改app.config.from_object('settings.Dev')就好,后面如果还有公用的,只需要来一个继承就好 路由系统- endpoint # 反向生成url,默认是函数名 - url_for('endpoint') # 就是反向解析,类似django的reverse # 如果没有参数,反向生成路由 url_for('endpoint')/url_for('index',nid=999) # 动态路由,得有参数接收一下 @app.route('/index/<int:nid>','POST']) def index(nid): print(nid) return 'Index' # 注意 ''' endpoint就是反向解析的name,不定义就是函数名 路由中可以跟参数,这个参数中间不要留空格,这个值是一个动态的,int表示的是什么类型 什么都不写就是字符串,但是不允许正则表达式 ''' 视图FBV和CBV FBV @app.route('/index/<int:nid>','POST']) def index(nid): print(nid) # 反向生成url print(url_for('/index',nid=999)) return 'Index' 请求相关的数据''' flask是直接引用,django是作为一个参数,内部处理机制完全不同 django请求数据是一个个的传递(门口有个人给了一塌子钱,同学一个个的传递过来) flask(直接扔到桌子上,自己导入去取) ''' @app.route('/index/<int:nid>','POST']) def index(nid): print(nid) # 请求相关信息 ''' request.method request.args request.form request.value request.cookies request.headers request.path request.full_path request.script_root request.url request.base_url request.host rrequest.files # 上传文件 obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filenaem(f.filename)) # 将上传的文件保存起来 ''' return 'Index' 响应# 响应相关的数据 # Json格式也可以返回 # dic = {'k': v1,'k2': v2} # 返回json格式的有这几种 # 1.json.dumps(dic) # 2.jsonify(dic) ''' return 'Index' return render_template() return redict() return json.dumps(dic) return jsonify(dic) 和django的JsonResponse类似 ''' 定制响应头/cookie # 响应头在哪写 # return 'Index' # 对于上面的这种数据,我们如果想要设置响应头,那么我们可以导入make_response,然后进行封装 obj = make_response('Index') obj.headers['xxx'] = '123' return obj # 对于剩下的几种格式都一样,我们都可以设置响应头 obj = make_response(jsonify(dic)) obj.headers['xxx'] = '123' return obj # 我们可以设置响应头,那么也可以设置cookie obj = make_response('Index') obj.headers['xxx'] = '123' obj.set_cookie('key','value') return obj 示例程序一from flask import Flask,render_template,redirect,session,url_for app = Flask(__name__) app.config.from_object('settings.DevelopmentConfig') STUDENT_DICT = { 1 : {'name': 'mm','age': 2,'gender': 'male'},2 : {'name': 'cc','age': 18,'gender': 'female'},3 : {'name': 'yy',} @app.route('/index') def index(): return render_template('index.html',stu_dic=STUDENT_DICT) @app.route('/info/<int:nid>') def detail(nid): info = STUDENT_DICT[nid] return render_template('detail.html',info=info) @app.route('/delete/<int:nid>') # 传过来是字符串,int还有个作用转成int def remove(nid): # 只需要保证路由一致就好,具体函数名可以不用管 STUDENT_DICT.pop(nid) print(STUDENT_DICT) return redirect(url_for('index')) # 直接跟函数名就好 if __name__ == '__main__': app.run() html页面<!--index页面--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>学生列表</h1> <table border="1"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>选项</th> </tr> </thead> <tbody> {% for key,value in stu_dic.items() %} <tr> <td>{{ key }}</td> <td>{{ value['name'] }}</td> <td>{{ value.get('ae','默认') }}</td> <td>{{ value.gender }}</td> <td> <a href="/info/{{key}}">详细信息</a> <a href="/add/{{key}}">添加</a> <a href="/delete/{{key}}">删除</a> </td> </tr> {% endfor %} </tbody> </table> </body> </html> <!--详细信息页面--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>学生详细</h1> <ul> <!--和python语法一模一样--> {% for item in info.values() %} <li>{{item}}</li> {% endfor %} </ul> </body> </html> <!--和python的使用一模一样,python怎么用他就怎么用--> 安全登录版本一@app.route('/login','POST']) # method默认是GET def login(): if request.method == 'GET': return render_template('login.html') user = request.form.get('user') pwd = request.form.get('password') if user == 'mcc' and pwd == '123': session['user'] = user # 将东西存到了cookie中 return redirect('/index') return render_template('login.html',**{'error': '用户名或者密码错误'}) @app.route('/index') def index(): if session.get('user'): return render_template('index.html',stu_dic=STUDENT_DICT) return redirect(url_for('login')) # 方式一:类似上述模式实现登录认证, 但是上述方式每一个函数都要写,我们用装饰器来做 知识点# 知识点一 import functools def auth(func): # @functools.wraps(func) # django是wraps(func) @functools.wraps(func) def inner(*args,**kwargs): # 其实最后执行的是inner ret = func(*args,**kwargs) return ret return inner @auth def login(): print('login') @auth def detail(): print('detail') print(login.__name__) # 不加 @functools.wraps(func)的时候是inner print(detail.__name__) # 加 @functools.wraps(func)的时候是detail # 知识点二 endpoint默认是函数名,那么如果同名了,怎么解决 def auth(func): def inner(*args,**kwargs) return ret return inner @auth def login(): print('login') @auth def detail(): print('detail') # 如果我们不加@functools.wraps(func)程序报错了 AssertionError: View function mapping is overwriting an existing endpoint function: inner # 这时候我们打印这个的函数发现我们的名字都是inner,没有指定endpoint,此时所有的都是inner print(login.__name__) # inner print(detail.__name__) # inner # 看看源码如何实现的? def route(self,rule,**options): # 入口 rule='/index' def decorator(f): # f就是我们传进来的func endpoint = options.pop("endpoint",None) # 别名 self.add_url_rule(rule,endpoint,f,**options) # 走这一步添加url return f return decorator @setupmethod def add_url_rule( self,endpoint=None,view_func=None,provide_automatic_options=None,**options ): if endpoint is None: endpoint = _endpoint_from_view_func(view_func) # 不写默认是函数名 options["endpoint"] = endpoint methods = options.pop("methods",None) # GET/POST if methods is None: methods = getattr(view_func,"methods",None) or ("GET",) if isinstance(methods,string_types): raise TypeError( "Allowed methods have to be iterables of strings," 'for example: @app.route(...,methods=["POST"])' ) methods = set(item.upper() for item in methods) # Methods that should always be added required_methods = set(getattr(view_func,"required_methods",())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. if provide_automatic_options is None: provide_automatic_options = getattr( view_func,"provide_automatic_options",None ) if provide_automatic_options is None: if "OPTIONS" not in methods: provide_automatic_options = True required_methods.add("OPTIONS") else: provide_automatic_options = False # Add the required methods now. methods |= required_methods rule = self.url_rule_class(rule,methods=methods,**options) rule.provide_automatic_options = provide_automatic_options ''' { endpoint:函数 ‘endpoint':inner函数 } ''' self.url_map.add(rule) if view_func is not None: # view_func我们自己的函数,就是inner # view_functions认为他就是一个字典,上面看 old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: # 不加functools.wrap(func)的报错信息 raise AssertionError( "View function mapping is overwriting an " "existing endpoint function: %s" % endpoint ) self.view_functions[endpoint] = view_func # 第一次进来‘函数名’=inner # 装饰器的先后顺序 # 装饰器需要放在路由的下面,这样子一进来先做的是路由的匹配,之后才会走装饰器,如果装饰器放在路由的上面,那么一进来就会连同下面的函数一起去进行装饰,不可理,所以会将装饰器放在视图函数的下面 装饰器适用--版本二# 方式二:类似上述模式实现登录认证, 但是上述方式每一个函数都要写,我们用装饰器来做 def auth(func): @functools.wraps(func) def inner(*args,**kwargs): # 其实最后执行的是inner if session.get('user'): ret = func(*args,**kwargs) return ret return redict(url_for('login')) return inner @app.route('/index') @auth def index(): return render_template('index.html',stu_dic=STUDENT_DICT) # 应用场景:比较少的函数中需要额外添加功能 版本三****实现权限管理基于before_request # 记住,当before_request返回的是None,是正常往下走,返回其他都是阻拦程序的运行 @app.before_request def mmmmrrrr(): # print('before_request') if request.path == '/login': return None return 'gong' # 当请求不是login的时候就会直接返回 # 所以我们直接使用这种方式,给函数批量的加入登录认证 @app.before_request def mmmmrrrr(): # print('before_request') if request.path == '/login': return None if session.get('user'): return None return redirect(url_for('login')) 模板的渲染--基本数据类型:可以执行python的语法,如:dict.get() list[‘xx‘] <!--模板支持类型--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--两种方式都可以支持--> {{ users.0 }} {{ users[0] }} {{ txt }} {{ func() }} # 和django的区别是django会自动加括号调用,而flask不会 {{ sb(6,3) }} # 模板全局设置函数的方式一 {{ 1|aa(2,4) }} # 模板全局设置函数的方式二 {% if 1|aa(2,4) %} # 这个可以放在if的后面作为条件 <div>999</div> {% else %} <div>7777</div> {% endif %} </body> </html> <!--模板继承--> <!--母版--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>模板</h1> {% block content %} {% endblock %} </body> </html> <!--继承母版--> {% extends "base.html"%} <!--两种方式都可以支持--> {% block content %} {{ users.0 }} {{ users[0] }} {{ txt }} {{ sb(6,3) }} {{ 1|aa(2,4) }} {% endblock %} <!--宏--> {% extends "base.html"%} <!--两种方式都可以支持--> {% block content %} {% macro aaa(name,type='text',value='') %} <h1>宏</h1> <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> <input type="submit" value="提交"> {% endmacro %} {{aaa('n1')}} 第一次调用 <!--可以将这种函数放在一个单独的页面中,在使用的时候直接加括号调用就好,类似django的inclution_tag--> {{aaa('n2')}} 第二次调用 {% endblock %} <!--静态文件的导入--> {% include 'form.html %} 模板后端的书写from flask import Markup # 这个就是django中的mark_safe @app.template_global() def sb(a,c): # {{sb(6,3)}} return a+c @app.template_filter() def aa(a,b,c): # {{1 | aa(2,4)}} 区别就是这个放在if后面做条件 return a+b+c @app.route('/tpl') def tpl(): contex = { 'users': ['longya','yyy','mcc'],'txt': Markup('<input type="text" />') } return render_template('tpl.html',**contex) sessionflask是将这个session对数据进行加密,保存到用户的浏览器的cookie中,其实session就是一个字典 ''' 当请求刚进来的时候,flask会帮我们读取cookie中session对应的值,将该值解密并反序列成为一个字典,放入内存,以便视图函数使用。 当请求结束时,flask会读取内存中字典的值,进行序列化+加密,写入到用户的cookie中 ''' # 使用 @app.route('/sess') def sess(): session['k1'] = '123' session['k2'] = '234' del session['k1'] return 'session' 闪现# 在sesson种存储一个数据,读取时通过通过pop将数据移除,只能存储取值一次 # 普通的session设置值,然后发现通过这种方式,session中的值会永久存在,如果我们设置了好多的session的话,那么就会一直存储下来 @app.route('/page1') def page1(): session['k1'] = '123' return 'session' @app.route('/page2') def page2(): print(session.get('k1')) return 'ok' # 基于上面的这种机制,flask引入了闪现,也就是flash,他会将session设置的值变成一个临时存储的值 from flask import session,get_flashed_messages @app.route('/page1') def page1(): flash('临时存储的数据','error') # 分类是error return 'session' @app.route('/page2') def page2(): print(get_flashed_messages(category_filter=['error'])) # 取分类是category_filter return 'ok' # flash内部源码实现原理 def flash(message,category="message"): # 从session中取值,取不到就给一个默认的空列表[] flashes = session.get("_flashes",[]) [1,2,3] # 支持分类,所以将分类和分类信息加入到列表中 flashes.append((category,message)) # 通过session设置从session中取到的值,所以设置的就是一个列表的值 session["_flashes"] = flashes # {'_flashes':[1,3]} # 信号暂时不用看 message_flashed.send( current_app._get_current_object(),message=message,category=category ) # get_flashed_messages内部源码实现原理 def get_flashed_messages(with_categories=False,category_filter=()): # flashes = _request_ctx_stack.top.flashes if flashes is None: _request_ctx_stack.top.flashes = flashes = ( session.pop("_flashes") if "_flashes" in session else [] ) # 通过session给pop出来了,出来的要么是个空,要么是一个空列表 if category_filter: flashes = list(filter(lambda f: f[0] in category_filter,flashes)) if not with_categories: return [x[1] for x in flashes] return flashes 中间件# 需求,想在程序启动之前做一些操作,启动之后做一些操作,通过中间件的形式 ''' 我们知道的是,当一个请求开始执行的时候,基于wsgi,走的是run_simple(localhost,port,执行的函数), 那么flask内部是怎么实现? ''' from flask import Flask app = Flask(__name__) @app.route('/index') def index(): return 'index' if __name__ == '__main__': app.run() # 开始启动flask,开始看执行流程 # 内部源码开始啦 def run(self,host=None,port=None,debug=None,load_dotenv=True,**options): ......# 感兴趣的可以看看,我们只看这下面的这一行 from werkzeug.serving import run_simple try: # 走的是run_simple方法,第三个参数就是当前对象,就是app,传的是对象的时候,会自动加括号调用,那么对象加括号调用走的是类的__call__方法。 run_simple(host,self,**options) finally: self._got_first_request = False # 直接搜索当前的__call__方法 def __call__(self,environ,start_response): # 执行的就是对象自己的wsgi_app方法 return self.wsgi_app(environ,start_response) # 所以我们想要实现在请求开始之前就做一些操作,我们可以考虑从这几个方面下手 ''' 1.直接修改源码 ---不好点就是同事也会拉取到你的代码,所以淘汰 2.重写__call__方法 --可以尝试 3.提供一种新的方法,具体请看下方 ''' # 第三种方法书写 class MiddleWare(object): def __init__(self,old): self.old = old def __call__(self,*args,**kwargs): print('来人了') return self.old(*args,**kwargs) if __name__ == '__main__': # 将对象的wsgi_app变成了自定义类的对象,此时的old就是原来的wsgi_app app.wsgi_app = MiddleWare(app.wsgi_app) # 那么后面在执行wsgi_app的时候,发现这个方法已经被我们给覆盖了,就会走我们书写的这个方法,这个时候他已经是一个对象了,对象+()走的就是call方法,那么此时我们在call方法里面做的操作就达到了在不修改源码的情况下增加了新功能。 app.run() # 这就是flask的中间件的执行,但是用的不多,主要用的还是before_request这些 特殊的装饰器****before_request *** after_request *** template_global template_filter before_first_request # 第一次请求的时候执行的,之后的时候都不会执行这个装饰器 errorhandler(404) # 用来返回错误日志的 # django1.9版本之前,中间件的执行顺序也是和flask一样,都是在request的时候返回一个return,就会在返回的时候从最后一个response返回这个请求,在1.9版本之后变成了从当前返回 @app.before_request def re2(): print('re2') @app.before_request def re3(): print('re3') @app.after_request def res1(response): print('res1') return response @app.after_request def res3(response): print('res3') # TypeError: after_request() takes 0 positional arguments but 1 was given 是因为没有接收response # TypeError: 'NoneType' object is not callable 是因为没有返回response return response # 定制错误页面 @app.errorhandler(404) # 用来捕获404错误的,返回一个自定义的错误页面 def error(response): print('303') return 'not found' @app.route('/index') def index(): return 'index' if __name__ == '__main__': app.run() # 如果有多个request和response,那么执行的顺序分为request和response # request,按照书写顺序从上到下依次执行 # response,按照书写顺序从下到上依次执行,进行了一个反转 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |