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里面点击设计表,能看到表的设计结构
联合索引也是普通索引
image
.
.
.
.
.
.
.
.
.
.
.
.

posted @   tengyifan  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示