flask:静态文件处理,localsetting配置,路由配置,CBV模式,模板,特殊装饰器
1.根据flask源码反推werkaueg的服务器的返回值
根据以下代码,找到返回值 from werkzeug.serving import run_simple def func(environ, start_response): print('请求来了') # 填充的代码 return .... if __name__ == '__main__': run_simple('127.0.0.1', 11111, func)
分析
前提: 在flask里面,程序启动的时候会执行app.__call__方法,__call__里面执行的run_simple是里面第三个参数加()来执行 所以单独了解werkzeug的运行流程时,可以对应flask的启动方法来进行查找zwerlzeug服务器的启动流程 # 第一步 # 执行app.run() # 程序启动,等待用户请求到来,就执行run_simple里面的第三个参数,其中self就是app() # 也就是执行app.__call__方法,其中__call__返回什么,用户 看到的就是什么。 # 第二步: # 其中__call__方法返回的是self.wsgi_app(environ, start_response),也就是说self.wsgi_app(environ, start_response)返回什么,用户看到的就是什么 # 所以我们在单独使用 werkzeug服务器的时候,相当于里面的func函数,我们只需要将func函数的返回值返回给用户就ok了 # 第三步: # 其中self.wsgi_app()函数返回的是response(environ, start_response),在该函数 # 内部我们可以看到response 等于的response = self.full_dispatch_request()的返回值 # 所以我们此时就是要将response=self.full_dispatch_request()返回值找到,就是用户的显示内容 #第四步 # 点进去self.full_dispatch_request方法,可以看到返回的是self.finalize_request(rv) # 其中full_dispatch_request里面的rv等于的就是我们第一步里面的函数的返回值,就是一个字符串,所以rv是什么,给用户返回的就是什么 # 第五步:我们来查看rv是什么 # 点击去self.finalize_request(rv)方法,我们可以看到response = self.make_response(rv),应该是将rv做了什么操作 # 然后赋值给了response # 第六步:我们查看返回的response是什么东西 # 点击去make_response函数,我们可以看到,在执行一些过滤rv的条件 # 唯一符合标准的就是if not isinstance(rv, self.response_class),可以看到rv = self.response_class(rv, status=status, headers=headers) # 那么response_class到底是什么? # 我们可以看到response_class是Response类,从from flask.wrappers import Response导入的。将鼠标放到该文件上,能看到该文件的路径,然后导入 # 那么rv是什么那?rv==self.response_class(rv, status=status, headers=headers)==Response(rv),rv是通过Response类进行一系列封装之后返回符合http协议的response响应 # 所以我们可以继承这个类,写在werkzeug服务器里面就能返回了 """ # from werkzeug.serving import run_simple # from flask.wrappers import Response # def func(environ, start_response): # print('请求来了') # # 填充的代码 # response = Response('你好') # 其中你好就等于rv ==Response(rv) # # return response(environ, start_response) # return response(environ, start_response) # if __name__ == '__main__': # run_simple('127.0.0.1', 11111, func) """ # 第七步 # 我们在Response类里面可以看到继承了ResponseBase, JSONMixin两个类。 # 其中ResponseBase是继承werkzeug服务器里面的BaseResponse类,所以等看到flask是基于werkzeug来作为wgsi服务器 # from werkzeug.wrappers.base_response import BaseResponse # 所以我们werkzeug服务器的代码要修改一下 """ # from werkzeug.serving import run_simple # from werkzeug.wrappers.base_response import BaseResponse # def func(environ, start_response): # print('请求来了') # # 填充的代码 # response = BaseResponse('你好') # # return response(environ, start_response) # return response(environ, start_response) # if __name__ == '__main__': # run_simple('127.0.0.1', 11111, func) """ 所以我们可以知道:werkzeug可以在不依赖flask的情况下,自己独立的作为一个web服务器来运行 所以我们可以知道flask是基于werkzeug服务器来充当web服务器的
2.静态文件
app = Flask(__name__) 一般都是用__name__,使用其他的字符串也可以,但是一般是不更改,因为有一个root_path和instance_path有关 root_path: 默认情况下,root_path通过Flask()实例化传入的__name__来计算程序的根目录 其中static_url_path和static_folder参数是和静态文件相关的 template_folder="设置模板路径" static_folder="static":表示静态文件的目录是static static_url_path="/yy":不写,默认就是static,但是和static_folder的static是不一样的。指的是想引入静态文件的时候,通过什么别名来找到文件 前端显示:<img src="/kobe/1.png" alt=""> 前端显示:<img src="{{ url_for('static',filename='1.png')}}"> 推荐使用
其中static指的是静态文件的目录 区别是: 使用第二种的时候,在static_url_path在发生变化的时候会自动的变化。个人认为这个参数是为了隐藏静态文件的路径。也可能是为了以后静态文件的迁移会方便一点
3.配置文件
pymysql信息写到配置文件 # https://blog.csdn.net/brightgreat/article/details/124636690
配置文件是通过app.config.from_object('config.setting')来引入的
基于全局变量的配置文件

settings:是服务器上的配置文件
localsettings:是本地开发是的配置文件
其中flask的加载的时候会自动的将配置文件加载进去,在setting里面将localsettings导入进去,就能在视图函数
中去读取配置文件的DB_HOST等配置。
这样写的好处是:
在以后我们上传代码时,可以不上传localsettings我们本地的数据库配置等文件,让运维人员通过我们文件的格式
在发布的时候,写在setting文件里面。这样视图函数也能获取到配置,并且有利于安全,不会好多人都知道密码
在使用git的时候,.ingnore文件来过滤掉不上传的文件,就可以将localsetting文件过滤掉
格式如下: config/localsettings.py 就过滤掉,不上传到缓存区!
或者也可以这样理解:
1.在settings.py里面写上我们本地开发的数据库账号密码,也导入localsettings.py文件
2,在代码上传的时候,也传入localsetting文件。在服务器上部署的时候,
3.在localsetting文件里面写上真实的数据库账号密码。这样也能保证安全
基于类的方式来引入配置文件

app.config.from_object('config.test1.DevTestings') # DevTestings是test1里面的类名
缺点:
得手动去改配置文件,上面基于去基于全局变量的就需要运维人员去改就可以了
关于配置文件
django的配置文件的方式只有基于全局变量这一种方式,也就是localsettings
flask可以鸡鱼类,也可以基于配置文件的方式
4.路由系统
源码分析: app = Flask(__name__) 1.在创建app对象的时候,将很多有用的信息都封装到对象里面了。可以点击去Flask()里面去查看 @app.route('/index') def index(): return "index" 2.在执行这段的时候,会将路由(url)和视图函数创建对应关系,存放到app的一个对象(map对象里面)里面。如果有多个url和视图的对应关系, 会将这多个对应关系都封装到app的对象里面。 注意: 2.1 在执行带参数的装饰器的时候,先执行的是@app.route('/index'),会返回一个函数,比如是f, 然后再将index传到f里面,f返回的是什么,index的返回值就是什么 2.2 所以先执行route()函数,其返回了一个decorator的函数名 2.3 在继续执行的时候,将index传递到decorator里面,相当于是一个闭包,其实也等价于 @decorator def index(): return "index" 2.4 在执行decorator的时候,会去取值endpoint,如果没有,就默认为None。 也会执行self.add_url_rule(rule, endpoint, f, **options) 其中rule是url,endpoint就是函数的endpoint,f就是被装饰的函数index 2.5 add_url_rule()方法首先会判断endpoint是否为空,为空的话,就将被装饰的函数名赋值给endpoint 2.6 add_url_rule里面会去执行一个rule = self.url_rule_class(rule, methods=methods, **options)类,创建了一个rule的对象, 其中里面的rule就是传递过来的url也就是index,methods就是能被url执行的请求方式的列表 url_rule_class()这一步做的就是将url和methods打包做成了一个rule对象 2.7 然后执行self.url_map.add(rule)将rule对象加入到map对象里面,说白了map对象有好多的ruel的规则,rule的里面有好多url和视图的对应关系 2.8 所以add_url_rule方法做的就是将url和methods封装成rule对选哪个,将rule对象放到map对象里面,map对象在app对象的url_map里面 3. 所以是decorator内部的add_url_rule()方法是将url和视图的对应关系放到flask里面的具体操作 4.所以是视图函数也可以这样写,也可以添加路由关系 from flask import Flask,request,render_template app = Flask(__name__) @app.before_request def f1(): print('befor') # @app.route('/index') def index(): print('123') return "456" app.add_url_rule('/index','index',index) #add_url_rule(rule, endpoint, f, **options) # 第一个/index是url,第二个是endpoint,第三个是函数名 if __name__ == '__main__': app.run( host='127.0.0.1', port='12122' ) 5.所以路由的加载流程是: 5.1 将url和methods封装成rule对象 5.2 将rule对象封装到map对象中 5.3 其中mao对象在app.url_map里面 5.4 app.url_map的内容如下: Map([<Rule '/index' (GET, HEAD, OPTIONS) -> index>, <Rule '/index' (GET, HEAD, OPTIONS) -> index>, <Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>] ) 动态路由: @app.route('/login') def login(): return render_template('login.html') @app.route('/login/<name>') def login(name): print(type(name)) return render_template('login.html') @app.route('/login/<int:name>') def login(name): print(type(name)) return render_template('login.html') 支持正则表达式的路由 from flask import Flask,render_template app = Flask(__name__) from werkzeug.routing import BaseConverter class RegConverter(BaseConverter): def __init__(self, map, regex): super().__init__(map) self.regex = regex app.url_map.converters['regex'] = RegConverter @app.route('/index/<regex("\d+"):x1>') def index(x1): return render_template('index.html') if __name__ == '__main__': app.run()
5.视图函数
FBV模式
第一种写法 from flask import Flask app = Flask(__name__) app.config.from_object('config.test1.DevTestings') @app.route('/index') def index(): return "hell world!" if __name__ == '__main__': app.run( host='127.0.0.1', port= 10089, ) 第二种写法 from flask import Flask,request,render_template app = Flask(__name__) @app.before_request def f1(): print('befor') # @app.route('/index') def index(): print('123') return "456" app.add_url_rule('/index','index',index) # 推荐 #add_url_rule(rule, endpoint, f, **options) # 第一个/index是url,第二个是endpoint,第三个是函数名 if __name__ == '__main__': app.run( host='127.0.0.1', port='12122' )
CBV模式
from flask import Flask,views,request app = Flask(__name__) def test(func): def inner(*args,**kwargs): print('befor') result = func(*args,**kwargs) print('after') return result return inner class UserView(views.MethodView): methods = ['GET','POST'] # 能过滤,添加上说明只支持get方法 decorators = [test,] # 相当于是装饰器,可以加多个装饰器,cbv加装饰器的方式 def get(self): print('get') return "get" def post(self): print('post') print(request.data) username = request.form.get('username') passwd = request.form.get('password') print(username,passwd) return "post" # CBV模式添加路由只能使用这样的方式,MethodView里面的eg # app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) # 其中counter代表url,CounterAPI代表试图的类,counter表示endpoint app.add_url_rule('/user', view_func=UserView.as_view('user')) # /user也可以添加参数 /user/<int:nid>,但是get方法得接受一下def get(self,nid): if __name__ == '__main__': app.run( host="127.0.0.1", port='54321' )
# 其原理与django的cbv模式是一样的,都是通过反射来实现的
6.模板
flask的模板可以传递字典,列表等,与django的模板方法类似
还可以传递函数名,在前端加括号执行
注意:如果想给所有的模板提供一个公共的方法
要在函数钱加一个装饰器@app.template_global()
视图函数
from flask import Flask,request,render_template app = Flask(__name__) def testa(name): return "dsb" + name
@app.route('/la') def la(): user = [11,22,33] return render_template('la.html',user=user,f=testa) pass if __name__ == '__main__': app.run( host='127.0.0.1', port='12321' )
la.html
{% extends 'lyout.html'%} {% block content %} {{user[0]}} {% for i in user %} <input type="text" value="{{i}}"> {% endfor %} {{f('科比')}} {% include 'head.html' %} {% endblock %}
head.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <h1>this is include</h1> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> </body> </html>
layout.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <h1>头</h1> {% block content%} {% endblock %} <h1>底</h1> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> </body> </html>
给所有的视图返回页面添加方(定义全局的模板方法)
from flask import Flask,request,render_template app = Flask(__name__) @app.template_global() # 全部模板都可以使用该函数,是全局的 前端调用方式{{ testa('科比')}} def testa(name): # 前端调用的也是相同的函数名 return "dsb" + name @app.template_filter() # 也是全局的, 前端调用方式{{'科比'|testb('18')’}} def testb(name,age): return "dsb" + name +age @app.route('/la') def la(): return render_template('la.html') if __name__ == '__main__': app.run( host='127.0.0.1', port='12322' )
总结:
1.相当于django的tag标签
2.因为是全局的,所以是全部视图函数都可以使用,但是在蓝图里面使用的话,就在本蓝图有效,应用范围不同
7.特殊的装饰器
注意:
类似与django的中间件,可以这样理解
视图函数
from flask import Flask,request,render_template app = Flask(__name__) @app.before_request # 一般用于用户登录认证,before_request也可以放在下面 def f1():
if request.path == '/login':
return # 返回空也是可以的,会继续向下走 print('befor') @app.after_request def f2(ss): print('after') return ss # 这里面的ss=response对象,是返回给用户的 @app.route('/index') def index(): print('123') return "123"
注意:如果after装饰的函数不返回值的话,会报错,f2() takes 0 positional arguments but 1 was given
因为:
按照django的中间件来看process_response里面的返回值也是给用户看的
所以before不写参数,没有返回值,因为一旦有返回值,就不会向后走了,就直接将before的返回值返回给用户了
但是after装饰的函数必须有返回值,因为是返回给用户看的
源码解析:
其实在flask为这两个'中间件'维护了两个列表
当flask程序从上向下执行的时候,会依次将被before装饰的函数放到列表中,当视图函数开始执行时,从before列表中从第0个元素的函数开始执行,直到最后一个,然后执行视图函数
当视图函数执行完之后,要执行after列表,也依次将读取到的程序加载到after列表中,只不过在执行after列表的时候,先将after列表resever(翻转),在执行列表里面的函数
before_request的其他调用(这样就是主动地调用before_request)
from flask import Flask,request,render_template app = Flask(__name__) @app.route('/index') def index(): print('123') return "123" # @app.before_request # 如果主动执行的话,函数上就不写装饰器了 def f1(): print('befor') app.before_request(f1) # xq = app.before_request(index) # 也可以将其赋值回来,但是也是函数的地址,可以点进去看 # print(xq) # print(index) 总结: 其实不管怎么使用before_request,其本质是一样的,都是将函数传递都before_request里面去执行,只不过这种方式是主动的执行
总结:
before_request和after_request在蓝图里面也是可以使用的。
但是其作用域也只是在本蓝图内有效,可以将before_requets和after_request定义到蓝图里面。
其他知识点:
threading.local()方法:给每一个线程开辟自己的空间,来存储自己的数据。
注意:
虽然flask没有threading.local(),但是flask内部实现了一个和threading.local()类似的功能~
源码
from threading import local import threading import time class Foo(object): def __init__(self): self.num = 0 # 当每个线程在执行 val1.xx=1 ,在内部会为此线程开辟一个空间,来存储 xx=1 # val1.xx,找到此线程自己的内存地址去取自己存储 xx,根据线程的id来进行区分线程。 val1 = local() val = Foo() def func(item): val1 = item val.num = val1 time.sleep(3) # print(val.num) print(val1) if __name__ == '__main__': for item in range(5): t = threading.Thread(target=func,args=(item,)) t.start()
pass
上帝说要有光,于是便有了光;上帝说要有女人,于是便有了女人!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类