flask之路【第三篇】各模块用法
⚙️本教程工程文件{ProjectName}:Practice_flask_0618
{ProjectName}:Practice_flask_0619
八、面向对象的上下文管理(with obj as f)
【注意这个上下文管理和flask上下文管理完全不是一回事。】
with的时候会执行__enter__函数,f会是__enter__函数的返回值。
执行完后,会执行__exit__函数
*利用这个可以将数据库池的连接和关闭分别放在enter和exit当中去
class Foo(object):
def __enter__(self):
return 123
def __exit__(self, exc_type, exc_val, exc_tb):
pass
obj = Foo()
with obj as f:
print(f)
class Foo(object):
def do_something(self):
pass
def close(self):
pass
class Context:
def __enter__(self):
self.data = Foo()
return self.data
def __exit__(self, exc_type, exc_val, exc_tb):
self.data.close()
with Context() as ctx:
ctx.do_something()
⚙️利用面向对象的上下文管理,来打开关闭数据库连接
#!sqhelper.py
class SqlHelper(object):
def __init__(self):
self.Pool = PooledDB(
creator = pymysql,
maxconnection = 6 ,# 最大连接数,0和None表示不限制
mincached =2, # 初始化最少创建的链接,0表示不创建
blocking = True, #链接池中如果没有可用,等待
ping = 0, #ping mysql服务,检查服务是否可用 # 0,代表不去检查, 1当表当请求是检查
host = '1270.0.01',
port = 3306,
user = 'root',
password = 'My@20241103',
db = 'db_name',
charset = 'utf8mb4',
)
def open(self):
conn = self.Pool.connection()
cursor = conn.cursor(cursor=cursors.DictCursor)
return conn,cursor
def close(self,cursor,conn):
cursor.close()
conn.close()
def fetchall(self,sql,*args):
conn,cursor = self.open()
cursor.execute(sql,args)
result = cursor.fetchall()
self.close(conn,cursor)
return result
def fetchone(self,sql,*args):
conn,cursor = self.open()
cursor.execute(sql,args)
result = cursor.fetchone()
self.close(conn,cursor)
return result
def __enter__(self):
return self.open()[1]
def __exit__(self,exc_tye,exc_val,exc_tb):
pass
db = SqlHelper()
#!使用数据库连接池.py
from sqhelp import db
db.fetchone("select sleep(1)")
db.fetchall("select sleep(1)")
with db as cur:
cur.execute("...") #省略号内自己填要执行的内容
result =cur.fechmany(4)
九、 flask的各模块
9.1 run_simple
flask框架基于 Werkzeug的wsgi(web服务网关接口)实现,flask自己没有wsgi
这段代码定义了一个简单的 WSGI 应用,使用
werkzeug
模块的run_simple
函数在本地主机的 5000 端口上运行。当接收到请求时,会打印 "请求来了"。
from werkzeug.serving import run_simple
def func(environ, start_response):
# print('请求来了')
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func)
可以看flask当中的app.run()和app.__call__方法,其实就是runsimple的实现。一步一步深入,最后发现还是调用的werkzeug的子类:如下是最终原始引用的实现(请求来了的时候,会调用app(),这个时候会执行___call__方法)
from werkzeug.serving import run_simple
from werkzeug.wrappers import BaseResponse
def func(environ, start_response):
print('请求来了')
response = BaseResponse('你好')
return response(environ, start_response)
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func)
等同于下述代码
""" 1. 程序启动,等待用户请求到来 app.run() 2. 用户请求到来 app() app.__call__ """ from flask import Flask app = Flask(__name__) @app.route('/') def index(): return '你好' if __name__ == '__main__': app.run()
9.2 静态文件-模板文件夹
flask源码中定义了静态文件、模板文件夹等内容路径名称

- 静态文件(路径对应关系,一般不这样改,使用默认的即可)

<body>
<h1>首页</h1>
<img src="/static/mm.jpg" /> # static_url_path = "/yy"时,这里要改成"/yy/mm.jpg" /
<img src="{{ url_for('static', filename='mm.jpg') }}" />
</body>
</html>
以上两种静态文件的引用地址效果是一样的,但推荐使用下边的方法:{{ url_for('static', filename='mm.jpg') (公司都用这种)
这种方法的优点是:static_url_path变的话,{{ url_for('static', filename='mm.jpg') 会自动跟着变,但上边的方法的src路径要手动跟着改
- 模板
9.3 配置文件
目录如下:
├──config
| └── settings.py
|
├── ...
...
#加载配置文件
app.config.from_oject("config.settings") # config文件夹下的settings.py文件
那么就可以通过app.config["DB_HOST"] 来调用配置,其中DB_HOST是setting中写的一个键值对的键
- (基于localsettings)如果项目配置有一千项,项目交到别人手里了,要改配置,不好找,那么可以增加一个文件localsettings.py目录如下
├──config
| ├── settings.py
| └──localsettings.py
├── ...
...
其中settings文件中新增如try except代码
DB_HOST = "192.168.1.1"
USER = "WANGYANG"
PWD = "12789"
# 新增如下代码
try:
form .localsettings import *
except ImportError:
pass
如果localsettings中有setting.py中相同的键值对时,将会覆盖原来的数据.(这会很方便不同的人去调试,因为每个人调试连接的账户密码等可能不同)
- 基于类建立配置文件(没有localsettings文件了)
9.4 路由系统
- 添加路由的两种方式:
def index():
return render_template('index.html')
app.add_url_rule('/index', 'index', index) #第一个参数url;第二个是endpoint;第三个是视图函数名
#--------------------------------------------
#公司一般用下边这种
@app.route('/login')
def login():
return render_template('login.html')
本质上@app.route("/login")就是调用的app.add_url_rule()函数。下边会介绍到调用它的流程
- 路由加载的源码流程
- 将url和函数打包成rule对象
- 将rule对象添加到map对象中
- app.url_map = map对象 (这个map对象中封装了很多路由,外部请求来了会从这里边匹配url,执行对应的函数)
- 动态路由(用<>包起来)
@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')
9.5 视图(FBV、CBV)
1. FBV:
FBV(Function-Based View)是基于函数的视图,是Django、Flask中处理HTTP请求的一种方式。在FBV中,视图是一个普通的Python函数,它接收一个HttpRequest对象作为参数,并返回一个HttpResponse对象。以下就是FBV
@app.route('/login')
def login():
return render_template('login.html')
2. CBV:
CBV(Class-Based View)是基于类的视图,是Django、Flask中另一种处理HTTP请求的方式。在CBV中,视图是一个Python类,通常继承自django.views.generic.View
,并通过定义类中的方法来处理不同的HTTP请求。这种方式更适合于复杂的Web应用,可以更好地利用面向对象编程的特性,如继承、多态等。
from flask import Flask, render_template, views
app = Flask(__name__)
def test1(func):
def inner(*args, **kwargs):
print('before1')
result = func(*args, **kwargs)
print('after1')
return result
return inner
def test2(func):
def inner(*args, **kwargs):
print('before2')
result = func(*args, **kwargs)
print('after2')
return result
return inner
class UserView(views.MethodView):
methods = ['GET', 'POST']
decorators = [test1, test2]
def get(self):
print('get')
return 'get'
def post(self):
print('post')
return 'post'
app.add_url_rule('/user', view_func=UserView.as_view('user')) # 括号里的user即endpoint
if __name__ == '__main__':
app.run()
9.6 模板
1 基本用法(继承、引用、传函数、传参数)
- 继承
母版中layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="/static/bootstrap5/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h2>书籍列表</h2>
{% block body %}
{% endblock %}
<h2>尾部</h2>
</body>
</html>
index.html (继承了layout.html)
{% extends "layout.html" %}
{% block body %}
<h2>内部</h2>
{% endblock %}
- inclue 引用其他html文件
另外还可以 {% include 'form.html' %} 插入其他的html 文件
比如form.html文件内容如下
<form action=""> <input type="text"> <input type="text"> <input type="text"> </form>
在index.html引用方式是:
{% extends "layout.html" %}
{% block body %}
<h2>内部</h2>
{% include 'form.html' %}
{% endblock %}
- 往模板传参数、传函数(主要说一下传函数)
比如后端有一个函数func
from flask import Flask, render_template
app = Flask(__name__)
def func(arg):
return '你好' + arg
@app.route('/md')
def index():
nums =
return render_template('md.html', nums=nums, f=func) # 传入了函数名字
if __name__ == '__main__':
app.run()
index.html模板中引用函数
{% extends "layout.html" %}
{% block body %}
<h2>内部</h2>
{% include 'form.html' %}
{{ f("王杨") }} <!-- 函数名字加括号,即是调用函数,函数中可以有参数 -->
{% endblock %}
2 定义全局模板方法
💡上边是往指定模板里边传方法。如果要给所有模板传公共的方法,需要用到全局变量
- 方法一(装饰器方法)
from flask import Flask, render_template
app = Flask(__name__)
@app.template_global() # {{ func("赵海宇") }} 全局模板方法
def func(arg):
return '海狗子' + arg
@app.template_filter() # {{ "赵海宇"|x1("孙宇") }} 全局模板过滤器方法
def x1(arg, name):
return '海狗子' + arg + name
@app.route('/md/hg')
def index():
return render_template('md_hg.html')
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>MDHG</h1>
{{ func("赵海宇") }}
{{ "赵海宇"|x1("孙宇") }}
</body>
</html>
💡注意在蓝图中注册这些模板方法时,只能在本蓝图范围内用这些方法。
- 方法二
def get_real_name():
userinfo = session.get("user_info")
return userinfo["username"]
# 在app文件或init初始化文件中,用app.template_global函数将你自己的函数放进去,然后所有模板就可以用了
app.template_global()(get_real_name)
9.7 特殊装饰器
类似django中的中间件
- @app.before_request 、@app.after_requeste充当中间件角色
from flask import Flask, render_template, request
app = Flask(__name__)
@app.before_request
def f1():
if request.path == '/login':
return
print('f1')
# return '123' # 不能有返回值
@app.after_request
def f10(response):
print('f10')
return response
@app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
多个特殊装饰器
from flask import Flask, render_template, request
app = Flask(__name__)
@app.before_request
def f1():
print('f1')
@app.before_request
def f2():
print('f2')
@app.after_request
def f10(response):
print('f10')
return response
@app.after_request
def f20(response):
print('f20')
return response
@app.route('/index')
def index():
print('index')
return render_template('index.html')
if __name__ == '__main__':
app.run()
app.__call__
✅以上特殊装饰器等同于下边的内容( 理解装饰器的本质 )
from flask import Flask, render_template, request
app = Flask(__name__)
@app.before_request
def f1():
if request.path == '/login':
return
print('f1')
# return '123' # 不能有返回值
#----------------------------------------------等同于下边代码
from flask import Flask, render_template, request
app = Flask(__name__)
def f1():
if request.path == '/login':
return
print('f1')
app.before_request(f1)
小细节
注意:before_after request可以在蓝图中定义,在蓝图中定义的话,作用域只在本蓝图。
from flask import Blueprint
x = Blueprint(__name__, 'x')
@x.before_request
def f1():
pass
@x.app_template_global()
def xxxxx():
return 'xxx'
@x.route('/index')
def index():
pass
@x.route('/index')
def index():
pass
@x.route('/index')
def index():
pass
💎内容回顾
- django和flask的区别
- 概括的区别
- django中提供功能列举
- 请求处理机制不同,django是通过传参的形式,flask是通过上下文管理的方式实现。
- wsgi
django和flask内部都没有实现socket,而是wsgi实现。wsgi是web服务网关接口,他是一个协议,实现它的协议的有:wsgiref/werkzeug/uwsgi
# django之前
from wsgiref.simple_server import make_server
def run(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [bytes('<h1>Hello, web!</h1>', encoding='utf-8')]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8000, run)
httpd.serve_forever()
# flask之前
from werkzeug.serving import run_simple
from werkzeug.wrappers import BaseResponse
def func(environ, start_response):
print('请求来了')
response = BaseResponse('你好')
return response(environ, start_response)
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func)
总结
- 核心概念:WSGI(Web Server Gateway Interface)是Python Web应用与Web服务器之间的标准接口。
- 实现方式:通过
wsgiref
、werkzeug
、uwsgi
等库实现。 - 示例代码:分别展示了使用WSGI在Django和Flask框架之前的简单应用实现。
-
web框架都有的功能:路由、视图、模板
-
before_request/after_request
- 相当于django的中间件,对所有的请求定制功能。
- template_global / template_filter
- 定制在所有模板中都可以使用函数。
- 路由系统处理本质 @app.route
- 将url和函数打包成rule,添加到map对象,map再放到app中。
- 路由
- 装饰器实现 / add_url_rule
- endpoint
- 动态路由
- 如果给视图加装饰器:放route下面、functools
- 视图
- FBV
- CBV(返回一个view函数,闭包的应用场景)
- 应用到的功能都是通过导入方式:如request/session
- flask中支持session
- 默认将session加密,然后保存在浏览器的cookie中。
- 模板比django方便一点
- 支持python原生的语法。
- 蓝图
- 帮助我们可以对很多的业务功能做拆分,创建多个py文件,把各个功能放置到各个蓝图,最后再将蓝图注册到flask对象中。
- 帮助我们做目录结构的拆分。
- threading.local对象
- 自动为每个线程开辟空间,让你进行存取值。
- 数据库链接池 DBUtils(SQLHelper)
- 面向对象上下文管理(with)【注意这个上下文管理和flask上下文管理完全不是一回事。】