flask----day05(flask内置信号, 自定义信号使用, django信号的使用,flask-script模块自定制命令,sqlalchemy 快速使用 创建与操作数据库中的表 )
昨日回顾
# 1 导出项目依赖 pipreqs
# 2 函数和方法的区别
# 3 local对象
-并发编程中的一个对象,它可以保证多线程并发访问数据安全
-本质原理是:不同的线程,操作的是自己的数据
-不支持协程
# 4 自己定义local类,支持线程和协程
# 注意点一:
try:
# 只要解释器没有装greenlet,这句话就会报错
# 一旦装了,无论使用不使用协程,
# 用getcurrent都能拿到协程id号,不使用协程,一个线程里面就只有一个协程,
# 也能拿到对应的协程号
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import get_ident
# 注意点二: 重写local类的 __setattr__和__getattr__
对象点不存在的名字的时候自动触发 __getattr__
给对象添加或者修改数据的时候自动触发 __setattr__
# 注意点三: 由于重写了__setattr__和__getattr__
在类内部使用 self.storage 会递归
使用类调用对象的方法,它就是普通函数,有几个值传几个值
object.__setattr__(self, 'storage', {})
等同于:self.storage={} 但会递归
等同于:setattr(self,'storage', {}) 但会递归
----------------------------------------------------------
----------------------------------------------------------
# 5 flask是如何实现这个local类的
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
----------------------------------------------
# 6 偏函数 :提前传值,返回一个对象,后期可以调用这个对象,传入后续的值
--------------------------------------------------------
# 7 请求上下文源码分析(ctx 对象),整个flask的执行流程
-一旦请求来了----》会执行 Flask类的对象app()---》触发Flask的 __call__-----
>self.wsgi_app(environ, start_response)
# Flask类wsgi_app 方法 大约 2417行
def wsgi_app(self, environ, start_response):
# 1 返回了一个ctx,请求上下文对象,RequestContext类的对象,
# 里面有当次请求的session,request
ctx = self.request_context(environ)
try:
try:
# 2 ctx.push---->RequestContext的push------382行
# _request_ctx_stack.push(self)------self是ctx
# _request_ctx_stack是全局变量
# _request_ctx_stack 是LocalStack()的对象
ctx.push()
# 匹配路由执行视图函数,请求扩展
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response) # 把结果返回给wsgi服务器
finally:
if self.should_ignore_error(error):
error = None
# 把当前放进去的ctx剔除,当次请求结束了
ctx.auto_pop(error)
-------------------------------------------
-是LocalStack()的对象的push方法 ,传入了ctx
def push(self, obj):
# self._local是 Flask自己定义的兼容线程和协程的Local对象
# 去self._local中反射 stack,会根据不同线程或协程,返回不同线程或协程的stack
# rv是None的
rv = getattr(self._local, "stack", None)
if rv is None:
# rv=[]
# self._local.stack = rv
# self._local={'协程id号1':{'stack':[]},'协程id号2':{'stack':[]}}
self._local.stack = rv = []
rv.append(obj)
#self._local={'协程id号1':{stack:[ctx,]},'协程id号2':{stack:[]}}
return rv
-------------------------------------------
- 在视图函数中:request = LocalProxy(partial(_lookup_req_object, "request"))
print(request.method) # 执行requets对象的 __getattr__
LocalProxy的__getattr__
return getattr(self._get_current_object(), name)
self._get_current_object()
# self._get_current_object()是ctx中的真正request对象,
# 通过反射拿真正request对象的method,自然就拿到当次请求的method
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
# 如果__local对象里面 没有 "__release_local__"字符串对应的属性名或方法名
# 一开始__local对象里面是没有 该字符串对应的属性或方法名的,所以执行下面的代码
# self.__local 是在LocalProxy类的__init__方法里面,放进去的
# LocalProxy类的__init__里面
# def __init__(self,local,name=None)
# object.__setattr__(self, "_LocalProxy__local", local),
# local是LocalProxy类初始化传入的
# local参数接收的就是LocalProxy类初始化传入的第一个数据
# request = LocalProxy(partial(_lookup_req_object, "request"))
# 所以 local就是 partial(_lookup_req_object, "request") 是偏函数
# __local是隐藏属性
# 所以在类外部要用该属性要通过_LocalProxy__local 才能拿到该隐藏的属性
# self.__local 拿LocalProxy类初始化的时候__setattr__方法执行的结果
# local也就是LocalProxy类初始化传入的 偏函数partial(_lookup_req_object, "request")
# 偏函数执行的结果,就是拿出当次请求的request对象
# getattr(当次请求的reuqest, 'method')
# 反射当次请求的request对象的 method属性
return self.__local() # self中的 __local,是隐藏属性
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
-------------------------------------------
def _lookup_req_object(name):
top = _request_ctx_stack.top # 这里把当前线程下 的ctx请求上下文对象取出来了
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name) # 去ctx中反射request,返回的就是当次请求的requets对象
# django flask 同步框架,部署的时候,使用uwsgi部署,uwsgi是进程线程架构,并发量不高
# 可以通过uwsgi+gevent,部署成异步程序
.
.
.
.
.
.
.
今日内容
1 信号
# Flask框架中的信号基于blinker模块,需要安装
# 其主要就是让开发者可是在flask请求过程中定制一些用户行为
flask 和django都有
pip3.8 install blinker
---------------------------------------------------------
# 信号:signial 翻译过来的
# 比如:用户表新增一条记录,就记录一下日志
方案一:在每个增加后,都写一行代码 ---》后期要删除,比较麻烦
方案二:使用信号,写一个函数,绑定内置信号,只要程序执行到这,就会执行这个函数
---------------------------------------------------------
# flask的 signals.py里面 有下面定义的这些内置信号
request_started = _signals.signal('request-started') # 请求到来前执行,和请求扩展功能差不多
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down')
# 请求执行完毕后自动执行(无论成功与否)
# 以下用的少一点:
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
# 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
---------------------------------------------------------
.
.
.
.
.
.
1.1 flask使用内置信号的步骤
-------------------------------------------------
# 使用内置信号的步骤
1 写一个函数
2 绑定内置信号
3 等待被触发
-------------------------------------------------
# 代码示范
from flask import Flask, render_template, signals
app = Flask(__name__)
app.secret_key = 'hhdohqoedhqoii'
# 1 定义函数
def test(*args, **kwargs):
print(args)
print(kwargs)
print('我执行了test------------')
# 2 绑定内置信号 内置信号不多,随意绑定一个 比如 渲染模板前触发
# 3 等待信号被触发
signals.before_render_template.connect(test)
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
.
.
.
.
.
# 使用自定义信号的步骤
# 1 定义出信号
# 2 写一个函数
# 3 绑定自定义的信号
# 4 触发信号的执行(咱们做)
---------------------------------------------
# 自定义信号代码实现
from flask import Flask, signals, session
from flask.signals import _signals
app = Flask(__name__)
app.secret_key = 'hoiij54511'
# 1 定义出信号 变量名自己起,括号里的字符串也是自己起的
xinghao = _signals.signal('xinghao')
# 2 写一个函数
def test2(*args, **kwargs):
print(args) # ('kkk',)
print(kwargs) # {'name': 'haha'}
print('session设置值---------------')
# 3 绑定自定义的信号
# 如果该行代码黑掉,自定义信号不绑定函数,信号就不会触发该函数
xinghao.connect(test2)
# 4 触发信号的执行(要自己做)
@app.route('/')
def hello_world():
session['lqz'] = 'lqz'
# 触发信号的执行,如果自定义信号不绑定函数,信号就不会被触发
xinghao.send('kkk', name='haha')
return 'hello world'
if __name__ == '__main__':
app.run()
------------------------------------------------------
# 源码里找信号如何触发的
# 1789行 full_dispatch_request方法里触发内置的,请求到来前执行的信号
def full_dispatch_request(self) -> Response:
try:
request_started.send(self) # 触发信号
# 1845行 finalize_request方法里触发内置的,请求结束后执行的信号
def finalize_request(self, rv,from_error_handler) -> Response:
response = self.make_response(rv)
try:
response = self.process_response(response) # 该方法里面执行了save_session方法
request_finished.send(self, response=response) # 触发信号
# 只要是内置的信号,在源码里面已经固定写死了,你只要在文件里面绑定对应的内置信号
# 就一定会触发对应的内置信号,进而执行对应的函数
.
.
.
.
.
.
.
.
.
1.2 django信号
# django中使用信号
参考博客 https://www.cnblogs.com/liuqingzheng/articles/9803403.html
# django提供了一些信号机制,就是观察者模式,又叫发布-订阅(Publish/Subscribe)
# 当发生一些动作的时候,发出信号,然后监听了这个信号的函数就会执行。
# 通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接收者。
# 用于在框架执行操作时解耦合
-------------------------------------------------
# 表模型相关
Model signals
pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发
pre_delete # django的modal对象删除前,自动触发
post_delete # django的modal对象删除后,自动触发
m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
----------------------------
# 数据库迁移相关
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
----------------------------
# 请求相关的
Request/response signals
request_started # 请求到来前,自动触发
request_finished # 请求结束后,自动触发
got_request_exception # 请求异常后,自动触发
----------------------------
# 数据库相关的
Database Wrappers
connection_created # 创建数据库连接时,自动触发
-------------------------------------------------
-------------------------------------------------
# django中使用内置信号
# 方式一
1 写一个函数
def callBack(*args, **kwargs):
print(args)
print(kwargs)
2 信号绑定函数
from django.db.models.signals import post_save
post_save.connect(callBack) # 这样表模型保存后,就会触发callBack函数
--------------------------------------
# 方式二
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save)
def my_callback(sender, **kwargs):
print("对象创建成功")
print(sender)
print(kwargs)
# 这样表模型保存前,就会触发my_callback函数
3 等待触发
-------------------------------------------------
django也可以使用自定义信号,参考下面博客 。
# django中使用信号
https://www.cnblogs.com/liuqingzheng/articles/9803403.html
-------------------------------------------------
.
.
.
.
.
.
.
.
.
2 flask-script模块 可以实现在flask项目里面自定制命令 执行对应的函数
# django中,项目启动命令
python manage.py runserver
----------------------------------------------
如果项目启动文件也做成了manage.py
那么在terminal里面也可以通过命令 python manage.py 启动项目
----------------------------------------------
# flask启动项目,想像djagno一样,通过命令启动,需要借助一个Flask_Script脚本
Flask==2.2.2
Flask_Script==2.0.3
----------------------------------------------
# 借助于:flask-script 实现
-安装:pip3.8 install flask-script==2.0.3
-修改代码:
from flask_script import Manager
manager=Manager(app)
manager.run()
-就可以用该命令启动了
python manage.py runserver
-------------------------------------------
# 代码示范:
from flask import Flask, render_template, signals, session
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app) # Manager类加括号实例化时,把app对象传进去
app.secret_key = 'hoiij54511'
@app.route('/')
def hello_world():
return 'hello world'
if __name__ == '__main__':
# app.run()
manager.run() # 用了Manager类加括号包app对象后,就不能用app.run()了
# 原来右键能启动,命令行里面输入 python manage.py 也能启动
# 现在用了flask_script后,就可以在命令行里面 python manage.py runserver 运行了
-------------------------------------------
-------------------------------------------
# flask自定制命令
from flask import Flask, render_template, signals, session
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app) # Manager类加括号实例化时,把app对象传进去
app.secret_key = 'hoiij54511'
#1 简单自定制命令
@manager.command
def custom(arg):
# 命令的代码,比如:初始化数据库, 或者有个excel表格,使用命令导入到mysql中等
print(arg)
# 2 复杂一些的自定制命令
# 以-n 后面的参数传给name 以-u 后面的参数传给url
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
print(name, url)
# 在terminal里 可以这样输入命令 python run.py cmd -n lqz -u xxx 触发cmd函数的运行
# 在terminal里 也可以这样输入命令 python run.py cmd --name lqz --url uuu 触发cmd函数的运行
@app.route('/')
def hello_world():
return 'hello world'
if __name__ == '__main__':
# app.run()
manager.run() # 用了Manager类加括号包app对象后,就不能用app.run()了
# 假设当前启动文件为run.py
# terminal里面敲命令:python run.py custom lqz 就能只运行被命令管理的custom函数,并传lqz进去
-----------------------------------------
.
.
.
.
django 中如何自定制命令
用空再补充!!!
.
.
.
.
.
.
.
.
.
3 sqlalchemy 快速使用
好多公司招聘需求,要求会sqlalchemy使用 重要!!!
# flask 中没有orm框架,需要使用第三方orm框架,方便我们快速操作数据库
# flask与fastapi web框架中,用sqlalchemy 第三方orm框架居多
# sqlalchemy 第三方orm框架 兼容性还可以,可以满足多个web框架使用
----------------------------------------------
# SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,
# 使用对象关系映射进行数据库操作,
# 简言之:将类和对象转换成SQL,然后执行SQL,并获取执行结果
----------------------------------------------
# 安装
pip3.8 install sqlalchemy
----------------------------------------------
# 了解
# SQLAlchemy本身无法操作数据库,其必须依赖pymsql等第三方插件,来操作数据库
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>?<options>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname?key=value&key=value...
----------------------------------------------
更多见博客:http://docs.sqlalchemy.org/en/latest/dialects/index.html
----------------------------------------------
.
.
.
.
.
4 sqlalchemy介绍和快速使用
4.1 原生操作的快速使用
# 使用原生sql,配合sqlalchemy
# 第一步 导入
import sqlalchemy
import pymysql
from sqlalchemy import create_engine
# 第二步 生成引擎,sqlalchemy自带数据库连接池
engin = create_engine(
'mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=10, # 连接被用10次后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用
)
# 第三步 使用引擎获取连接,操作数据库
conn = engin.raw_connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select id,name,`desc` from app01_article where id<3')
res = cursor.fetchall()
print(res)
.
.
.
.
.
.
5 sqlalchemy创建与操作数据库中的表
# 使用sqlalchemy目的是: 为了做对象关系映射,能根据orm语句,在数据库中创建出表
models.py文件
# 使用sqlalchemy 创建表模型
# 第一步 导入模块
from sqlalchemy import create_engine
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
# 第二步 执行declarative_base,得到一个基类
Base = declarative_base()
# 第三步 继承生成的Base类
class User(Base):
# 第四步 写字段并指定类型
id = Column(Integer, primary_key=True) # Column是生成一列的关键字
# varchar类型32长度,该字段添加索引,nullable=False 不允许为空
name = Column(String(32), index=True, nullable=False)
email = Column(String(32), unique=True)
# datetime.datetime.now # 不能加括号,加了括号以后永远是当前时间
ctime = Column(DateTime, default=datetime.datetime.now)
extra = Column(Text, nullable=True) # 文本类型不能为空
# 第五步 写表名 如果不写以类名为表名
__tablename__ = 'users'
# 第六步 建立联合索引,建立联合唯一
__table_args__ = (
UniqueConstraint('id', 'name', name='uix_id_name'),
Index('ix_id_name', 'name', 'email', )
# uix_id_name 就是联合唯一的名字 ix_id_name 就是联合索引的名字
)
class Book(Base):
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=False)
__tablename__ = 'books'
# 第七步 把表同步到数据库中
# 还是要先生成引擎
# 不会创建库,只会生成表,会在你配置的库里面生成表
engine = create_engine(
'mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=10, # 连接被用10次后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用
)
# 把表同步到数据库命令 (把被Base管理的所有表(继承base的表),都同步到数据库)
# 这样只要右键运行该py文件,就可以直接将表同步到数据库了
Base.metadata.create_all(engine)
-------------------------------------------------------
# 注意原生的sqlalchemy是不能修改字段的!!!!!!
# 所以已经同步到数据库后,想增加或删除表里面的字段,再右键运行,是没有效果的!!!
# 需要借助第三方才能实现像django一样,增加删除字段的!
# 把所有表删除
# Base.metadata.drop_all(engine)
# 右键运行 就能把被Base管理的所有表删除
navicat里面点击设计表,能看到表的设计结构
联合索引也是普通索引
.
.
.
.
.
.
.
.
.
.
.
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!