Loading

hellohelp

flask之路【第五篇】扩充知识点

十二、扩充知识点

12.1内容补充

  1. getitem、setitem、setattr、getattr
class Foo:
    #

obj = Foo()
obj()  # __call__

obj[x1] = 123  # __setitem__
obj[x2]  # __getitem__

obj.x1 = 123  # __setattr__
obj.x2  # __getattr__
  1. 从看flask源码你学到了什么?
  • 新的编程思路。
    • django、drf数据是通过传递。
    • flask,存储在某个地方,以后用的时候去拿。
      哪种好?两个不同的实现机制,没有好坏之分。
      django好,疑问如果是一个初学者对于django的机制比较好理解,flask学习代价比较大(了解上下文管理机制之后才能更好的理解)。
  • 技术点
    • 单利模式的应用场景
    • LocalProxy
    • 装饰器不注意functools
  1. 在flask的Local对象中为什么要通过线程ID进行区分?
    因为在flask中可以开启多线程的模式,当开启多线程模式进行处理用户请求时,需要将线程之间的数据进行隔离,以防止数据混乱。

  2. 在flask的Local对象中为什么要维持成一个栈?

{
111:{stack:[ctx, ]},
}
在web runtime 时,栈永远只有1个对象。
{
111:{stack:[ctx, ]}
112:{stack:[ctx, ]}
}
{
111:{stack:[app_ctx, ]}
112:{stack:[app_ctx, ]}
}

在写离线脚本时,才会用在栈中放多个对象。(创建一个py文件本地运行)

from flask import current_app, g
from pro_excel import create_app
app1 = create_app()
with app1.app_context():  ApplicationContext对象(app, g) -> local对象
print(current_app.config) app1
app2 = create_app()
with app2.app_context():  ApplicationContext对象(app, g) -> local对象
print(current_app.config)
print(current_app.config)

写离线脚本且多个上下文嵌套时,才会在栈中添加多个对象。
注意:在flask中很少出现嵌套的脚本。

12.2 信号

信号里边的函数都没有返回值,但是可以将要传递的值放在g中,等到有地方返回的时候再返回

  1. 信号
    信号,是在flask框架中为我们预留的钩子,让我们可以进行一些自定义操作。
pip install blinker

根据flask项目的请求流程来进行设置扩展点

  • 中间件
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/index')
def index():
    return render_template('index.html')
@app.route('/order')
def order():
    return render_template('order.html')
class MyMiddleware(object):
    def __init__(self, old_app):
        self.wsgi_app = old_app.wsgi_app
    def __call__(self, *args, kwargs):
        print('123')
        result = self.wsgi_app(*args, kwargs)
        print('456')
        return result
app.wsgi_app = MyMiddleware(app)
if __name__ == '__main__':
    app.run()
  • 当app_ctx被push到local中栈之后,会触发appcontext_pushed信号,之前注册在这个信号中的方法,就会被执行。

    from flask import Flask, render_template
    from flask import signals
    app = Flask(__name__)
    @signals.appcontext_pushed.connect
    def f1(arg):
        print('appcontext_pushed信号f1被触发', arg)
    @signals.appcontext_pushed.connect
    def f2(arg):
        print('appcontext_pushed信号f2被触发', arg)
    @app.route('/index')
    def index():
        return render_template('index.html')
    @app.route('/order')
    def order():
        return render_template('order.html')
    if __name__ == '__main__':
        app.run()
        app.__call__
    
  • 执行before_first_request扩展

from flask import Flask, render_template
app = Flask(__name__)
@app.before_first_request
def f2():
    print('before_first_requestf2被触发')
@app.route('/index')
def index():
    return render_template('index.html')
@app.route('/order')
def order():
    return render_template('order.html')
if __name__ == '__main__':
    app.run()
  • request_started信号
from flask import Flask, render_template
from flask import signals
app = Flask(__name__)
@signals.request_started.connect
def f3(arg):
    print('request_started信号被触发', arg)
@app.route('/index')
def index():
    return render_template('index.html')
@app.route('/order')
def order():
    return render_template('order.html')
if __name__ == '__main__':
    app.run()
  • url_value_processor

    from flask import Flask, render_template, g
    from flask import signals
    app = Flask(__name__)
    @app.url_value_preprocessor
    def f5(endpoint, args):
      print('f5')
    @app.route('/index/')
    def index():
      print('index')
      return render_template('index.html')
    @app.route('/order')
    def order():
      print('order')
      return render_template('order.html')
    if __name__ == '__main__':
      app.run()
    
  • before_request

    from flask import Flask, render_template, g
    from flask import signals
    app = Flask(__name__)
    @app.before_request
    def f6():
      g.xx = 123
      print('f6')
    @app.route('/index/')
    def index():
      print('index')
      return render_template('index.html')
    @app.route('/order')
    def order():
      print('order')
      return render_template('order.html')
    if __name__ == '__main__':
      app.run()
    
  • 视图函数

  • before_render_template / rendered_template

from flask import Flask, render_template, g
from flask import signals
app = Flask(__name__)
@signals.before_render_template.connect
def f7(app, template, context):
    print('f7')
@signals.template_rendered.connect
def f8(app, template, context):
    print('f8')
@app.route('/index/')
def index():
    return render_template('index.html')
@app.route('/order')
def order():
    print('order')
    return render_template('order.html')
if __name__ == '__main__':
    app.run()
  • after_request

    from flask import Flask, render_template, g
    from flask import signals
    app = Flask(__name__)
    @app.after_request
    def f9(response):
      print('f9')
      return response
    @app.route('/index/')
    def index():
      return render_template('index.html')
    @app.route('/order')
    def order():
      print('order')
      return render_template('order.html')
    if __name__ == '__main__':
      app.run()
    
  • request_finished

     from flask import Flask, render_template, g
      from flask import signals
      app = Flask(__name__)
      @signals.request_finished.connect
      def f10(app, response):
        print('f10')
      @app.route('/index/')
      def index():
        return render_template('index.html')
      @app.route('/order')
      def order():
        print('order')
        return render_template('order.html')
      if __name__ == '__main__':
        app.run()
    
    
  • got_request_exception

from flask import Flask, render_template, g
from flask import signals
app = Flask(__name__)

@app.before_first_request
def test():
    int('asdf')

@signals.got_request_exception.connect
def f11(app, exception):
    print('f11')

@app.route('/index/')
def index():
    return render_template('index.html')

@app.route('/order')
def order():
    print('order')
    return render_template('order.html')

if __name__ == '__main__':
    app.run()
  • teardown_request
from flask import Flask, render_template, g
from flask import signals
app = Flask(__name__)
@app.teardown_request
def f12(exc):
  print('f12')
@app.route('/index/')
def index():
  return render_template('index.html')
@app.route('/order')
def order():
  print('order')
  return render_template('order.html')
if __name__ == '__main__':
  app.run()


  • request_tearing_down
from flask import Flask, render_template, g
from flask import signals
app = Flask(__name__)

@signals.request_tearing_down.connect
def f13(app, exc):
    print('f13')

@app.route('/index/')
def index():
    return render_template('index.html')

@app.route('/order')
def order():
    print('order')
    return render_template('order.html')

if __name__ == '__main__':
    app.run()
  • appcontext_popped
from flask import Flask, render_template, g
from flask import signals
app = Flask(__name__)

@signals.appcontext_popped.connect
def f14(app):
    print('f14')

@app.route('/index/')
def index():
    return render_template('index.html')

@app.route('/order')
def order():
    print('order')
    return render_template('order.html')

if __name__ == '__main__':
    app.run()

总结:关于flask内部共有14+个扩展点用于我们对flask框架内部进行定制,其中有:9个是信号。

template_rendered = _signals.signal("template-rendered")
before_render_template = _signals.signal("before-render-template")
request_started = _signals.signal("request-started")
request_finished = _signals.signal("request-finished")
request_tearing_down = _signals.signal("request-tearing-down")
got_request_exception = _signals.signal("got-request-exception")
appcontext_tearing_down = _signals.signal("appcontext-tearing-down")
appcontext_pushed = _signals.signal("appcontext-pushed")
appcontext_popped = _signals.signal("appcontext-popped")

######  message_flashed  给flash赋值会触发这个信号。
message_flashed = _signals.signal("message-flashed")

12.3 扩展:flash

(flash存值之后只能取一次)其实这个值是存在session中的,用session一样可以实现。

from flask import Flask, render_template, flash, get_flashed_messages, session
from flask import signals
app = Flask(__name__)
app.secret_key = 'iuknsoiuwknlskjdf'

@app.route('/index/')
def index():
    # flash('123')
    session['k1'] = 123
    return render_template('index.html')

@app.route('/order')
def order():
    # messages = get_flashed_messages()
    # print(messages)
    val = session['k1']
    del session['k1']
    print(val)
    return render_template('order.html')

if __name__ == '__main__':
    app.run()

💎💎flash的显示,弹窗**

后端:flash('密码已成功重置!请使用新密码登录。', 'success')

前端:

{% with messages = get_flashed_messages(with_categories=true) %}
     {% if messages %}
        <script>
        {% for category, message in messages %}
            alert("{{ message }}");  // 使用 JavaScript 弹窗
        {% endfor %}
        </script>
        {% endif %}
{% endwith %}

12.4 flask-script

Flask-Script 是一个用于处理命令行脚本的扩展,高版本已经弃用

高版本的 Flask 弃用了 Flask-Script,主要是为了降低维护成本、避免功能重复,并简化生态系统。这使得 Flask 项目更加轻量和高效,同时也为开发者提供了更好的开发体验。

社区中出现了更加成熟和功能丰富的命令行扩展,如 Flask-Cli,这些扩展提供了更多的功能和更好的用户体验。

💎推荐使用argparse模块

12.5 蓝图划分

目录结构的划分

  • 分功能蓝图:

image-20250714210117129

  • 分结构蓝图:bigblue

image-20250714211150134

十三、SqlHelper的实现

以下两种实现都可以用,两种不同的机制单例模式(借助threadinglocal实现)和多例模式:

  • 方式一(单例 参见project:Practice0701)sqlhelper.py
import pymysql
from dbutils.pooled_db import PooledDB
from pymysql import cursors
import threading
'''
    storage = {
    1111: {'stack': []}
    }
'''

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='127.0.01',
            port=3306,
            user='root',
            password='My@20241103',
            db='db_name',
            charset='utf8mb4',

        )
        self.local = threading.local()

    def open(self):
        conn = self.pool.connection()
        cursor = conn.cursor()
        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):
        conn, cursor = self.open()
        rv = getattr(self.local, 'stack', None)
        if not rv:
            self.local.stack = [(conn, cursor)]
        else:
            rv.append((conn, cursor))
            self.local.stack = rv
        return cursor

    def __exit__(self, exc_type, exc_val, exc_tb):
        rv = getattr(self.local, 'stack', None)
        if not rv:
            # del self.local.stack
            return
        conn, cursor = self.local.stack.pop()
        cursor.close()
        conn.close()


db = SqlHelper()

调用:

from sqlhelper import db

# db.fetchall(...)
# db.fetchone(...)

with db as c1:
    c1.execute('select 1')
    with db as c2:
        c1.execute('select 2')
print(123)
  • 方式二
'''
    通过全局变量的形式。实现连接,关闭连接是通过每个实例的close关闭。
    上下文管理器简化了数据库操作,确保了连接的正确释放,同时连接池机制优化了资源利用率。
    不能用单例模式
'''
import pymysql
from dbutils.pooled_db import PooledDB
from pymysql import cursors
POOL = PooledDB(
            creator=pymysql,
            maxconnection=6,  # 最大连接数,0和None表示不限制
            mincached=2,  # 初始化最少创建的链接,0表示不创建
            blocking=True,  # 链接池中如果没有可用,等待
            ping=0,  # ping mysql服务,检查服务是否可用 # 0,代表不去检查, 1当表当请求是检查
            host='127.0.0.1',
            port=3306,
            user='root',
            password='My@20241103',
            db='db_name',
            charset='utf8mb4',

        )

class SqlHelper(object):
    def __init__(self):
        self.conn = None
        self.cursor = None

    def open(self):
        conn = POOL.connection()
        cursor = conn.cursor()
        return conn, cursor

    def close(self):
        self.cursor.close()
        self.conn.close()

    def __enter__(self):
        self.conn, self.cursor = self.open()
        return self.cursor

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()


# ########   使用   #########
with SqlHelper() as c1:   #,with SqlHelper()会执行里边的__enter__方法并将返回值会给c1。with块代码执行完以后会执行SqlHelper()的__exit__方法。
    c1.execute('select 1')
    with SqlHelper() as c2:
        c2.execute('select 2')
    print(666)                   

with SqlHelper() as cursor:
    cursor.execute('select 1')

with SqlHelper() as cursor:
    cursor.execute('select 1')

其他python知识点:类的特殊方法

💎python对象小知识点__call__方法:

​ 在Python中,如果类的实例对象后面加上括号(即对象()), 这会触发执行该类中的__call__方法

class Foo:
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print(f"Calling {self.name} with __call__ method")

obj = Foo("test")
obj()  # 输出:Calling test with __call__ method
#  在这个例子中,当调用obj()时,会执行Foo类中的__call__方法,并打印相应的消息

💎python对象小知识点__init__ 方法

​ 一个特殊的方法,通常被称为构造函数。它在创建类的实例时自动调用,并用于初始化对象的状态。通过 __init__ 方法,你可以定义一个对象被创建时应该拥有的属性。

class Dog:
    def __init__(self, name, age):
        self.name = name  # 初始化name属性
        self.age = age    # 初始化age属性

    def bark(self):
        return f"{self.name} says woof!"

# 创建Dog类的一个实例
my_dog = Dog("Buddy", 3)

# 访问实例的属性和方法
print(my_dog.name)  # 输出: Buddy
print(my_dog.age)   # 输出: 3
print(my_dog.bark()) # 输出: Buddy says woof!

在这个例子中,Dog 类有一个 __init__ 方法,它接受两个参数 nameage,并在创建 Dog 的实例时将这些值赋给实例的 nameage 属性。这样每个 Dog 实例都会有自己的 nameage 属性。

💎__init____call__区别:

​ 在创建类的实例时,只有 __init__ 方法会被自动调用,而 __call__ 方法则是在实例被当作函数调用时才会被调用。

参数

  • __init__ 方法:通常接受一个或多个参数,用于初始化对象的属性。第一个参数通常是 self,代表实例本身5。
  • __call__ 方法:可以接受任意数量的参数,这些参数是在调用实例时传递的。

返回值

  • __init__ 方法:通常不返回任何值,因为它主要用于初始化对象。
  • __call__ 方法:可以返回一个值,就像普通函数一样。
class MyClass:
    def __init__(self, value):
        self.value = value  # 初始化实例属性

    def __call__(self, arg):
        return self.value + arg  # 在调用实例时执行的操作

# 创建实例
my_instance = MyClass(10)  # __init__ 被调用

# 调用实例
result = my_instance(5)  # __call__ 被调用,返回 15

其他python知识点: 读取excel 内容放入数据库

(xlrd模块,只能处理.xls文档)

import xlrd

@od_bp.route('/upload', methods=["GET", "POST"])
def upload():
    if 'excel_file' not in request.files:
        return "没有选择文件", 400

    file = request.files['excel_file']
    if file.filename == '':
        return "没有选择文件", 400

    if file:
        if '.' in file.filename and \
               file.filename.rsplit('.', 1)[1].lower() in {'xls', 'xlsx'}:
            try:
                # 直接处理上传的文件内容
                file_data = file.read()
                workbook = xlrd.open_workbook(file_contents=file_data)
                sheet = workbook.sheet_by_index(0)

                for row_idx in range(1, sheet.nrows):
                    row_data = sheet.row_values(row_idx)
                    author = row_data[2]
                    title = row_data[0]
                    price = row_data[1]
                    db.insert_one(
                        "INSERT INTO `book` (author, title, price, author_id) VALUES (%s, %s, %s, 2)",
                        (author, title, price)
                    )

                return redirect("/index")
            except Exception as e:
                return f"处理失败: {str(e)}", 500
        else:
            return "无效的文件类型", 400
    else:
        return "无效的文件类型", 400
<form action="/upload" method="post" enctype="multipart/form-data">
                <input type="file" name="excel_file" accept=".xls,.xlsx" required>
                <input type="submit" class="btn btn-primary" value="导入书籍">

            </form>

总结一下xlrd用法:

work_book =xlrd.open_workbook(file_path)   #传入file_path或者文件对象,获得工作簿对象
sheet1 = work_book.sheet_by_index(0)         #获取工作表
for row_idx in range(1,sheet1.nrows) 
	row_data = sheet1.row_values(row_idx)   # 获取某一行的值,是一个列表   
    
posted @ 2025-07-15 13:07  HordorZzz  阅读(11)  评论(0)    收藏  举报