Loading

hellohelp

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源码中定义了静态文件、模板文件夹等内容路径名称

![屏幕截图 2025-06-27 123045](C:\Users\lx\Pictures\Screenshots\屏幕截图 2025-06-27 123045.png)

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

![屏幕截图 2025-06-27 124425](C:\Users\lx\Pictures\Screenshots\屏幕截图 2025-06-27 124425.png)

<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文件了)

image-20250627225103303

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

💎内容回顾

  1. django和flask的区别
  • 概括的区别
  • django中提供功能列举
  • 请求处理机制不同,django是通过传参的形式,flask是通过上下文管理的方式实现。
  1. 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服务器之间的标准接口。
  • 实现方式:通过wsgirefwerkzeuguwsgi等库实现。
  • 示例代码:分别展示了使用WSGI在Django和Flask框架之前的简单应用实现。
  1. web框架都有的功能:路由、视图、模板

  2. before_request/after_request

  • 相当于django的中间件,对所有的请求定制功能。
  1. template_global / template_filter
  • 定制在所有模板中都可以使用函数。
  1. 路由系统处理本质 @app.route
  • 将url和函数打包成rule,添加到map对象,map再放到app中。
  1. 路由
  • 装饰器实现 / add_url_rule
  • endpoint
  • 动态路由
  • 如果给视图加装饰器:放route下面、functools
  1. 视图
  • FBV
  • CBV(返回一个view函数,闭包的应用场景)
  • 应用到的功能都是通过导入方式:如request/session
  1. flask中支持session
    • 默认将session加密,然后保存在浏览器的cookie中。
  2. 模板比django方便一点
    • 支持python原生的语法。
  3. 蓝图
    • 帮助我们可以对很多的业务功能做拆分,创建多个py文件,把各个功能放置到各个蓝图,最后再将蓝图注册到flask对象中。
    • 帮助我们做目录结构的拆分。
  4. threading.local对象
    • 自动为每个线程开辟空间,让你进行存取值。
  5. 数据库链接池 DBUtils(SQLHelper)
  6. 面向对象上下文管理(with)【注意这个上下文管理和flask上下文管理完全不是一回事。】
posted @ 2025-07-15 13:04  HordorZzz  阅读(14)  评论(0)    收藏  举报