flask的进阶使用2

1 cbv加装饰器

from flask import Flask
from flask.views import MethodView

app = Flask(__name__)

app.debug = True


### 登录认证--->不能公用--》要么只能给fbv用,要么只能给cbv用
def auth(func):
    def inner(*args, **kwargs):
        print(args) #cbv,会有第一个参数 self  # 如果是fbv,就是空的
        res = func(*args, **kwargs)
        print('装饰器走了')
        return res

    return inner


@app.route('/', endpoint='index')
@auth
def index():
    return 'hello flask'


# class UserView(MethodView):
#     @auth
#     def get(self):
#         return 'user-get'
class UserView(MethodView):
    decorators = [auth]  # 顺序从下往上
    #methods=['POST']
    def get(self):
        return 'user-get'


app.add_url_rule('/user', endpoint='user', view_func=UserView.as_view('user'))
if __name__ == '__main__':
    app.run()

2 闪现(flash)

2.1Flask中的使用

# 1 flask中得闪现存放数据的地方,一旦取了,数据就没了
	-实现跨请求间传递数据
# 2 django中有没有类似的东西?
	message 消息框架
    
    
# 3 基本使用
	1 设置:flash('欢迎你:lqz')
    2 取:get_flashed_messages()
    
# 4 根据标签设置和取值
	flash('超时错误',category="x1")
	get_flashed_messages(category_filter=['x1'])
from flask import Flask, request, render_template, redirect,flash,get_flashed_messages

app = Flask(__name__)

app.debug = True
# 要用闪现,必须指定secret_key--》闪现内部使用session实现的
app.secret_key='asdfasdf'


@app.route('/login', endpoint='login',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'lqz' and password == '123':
            # 使用闪现,放个数据
            flash('欢迎你:lqz')
            flash('ss')
            flash('ee')
            return redirect('/')
        else:
            flash('用户名密码错误')
            return redirect('/')

@app.route('/')
def index():
    # 从闪现中取出数据
    # print(get_flashed_messages())
    return render_template('index.html')

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

  • login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<div>
    <form method="post">
        <div>
            <p><label>用户名:</label>
            <input type="text" name="username"></p>
            <p><label>密码:</label>
            <input type="password" name="password"></p>
            <p><input type="submit" value="提交"></p>
        </div>
    </form>
</div>

</body>
</html>
  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<h1>首页</h1>
<h2>{{ get_flashed_messages() }}</h2>

</body>
</html>
image-20240613163411666

2.2 django中使用

################ 1 基础配置############
INSTALLED_APPS = [
    ...
    'django.contrib.messages',
    ...
]
# 在django setting.py 取消注释的message app
MIDDLEWARE = [
    ...
    'django.contrib.messages.middleware.MessageMiddleware',
    ...
]
# 在django setting.py 取消注释的message 的中间件
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...
                'django.contrib.messages.context_processors.messages',
            ],
        }
    }
]
################ 2 设置存放位置############
MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"


################ 3 放入值############
#添加message
from django.contrib import messages
def concel_order(request):
    messages.add_message(request, messages.SUCCESS, "删除成功1")
    messages.add_message(request, messages.SUCCESS, "删除成功2")
    
    return redirect("/order/control/")

################ 4 视图函数中取############
# 在视图函数中添加messages模块
# 再通过messages.add_message导入提示信息
# 在视图函数中import get_messages模块获取添加的提示信息
def control_order(request):
    if request.method == "GET":
        from django.contrib.messages.api import get_messages
        m1 = get_messages(request)
        print(m1)
        
################ 5 模板中取############
# 在html模板中添加for循环拿到message
<div>
    {% for obj in messages %}
        <ul>{{ obj.message }}</ul>
    {% endfor %}
</div>

3 g对象

  • g在当次请求中,可以放入值,可以取出值
# 1 g 对象,是一个全局对象--》global的缩写,global是关键字,不能用来做变量,所以它叫了g
# 2 g在当次请求中,可以放入值,可以取出值
	-我们使用:index 视图函数---》内部又调用了add--》add()又调用了aa()-->如果有参数,需要依次传入
    -通过g对象,我们可以把参数放到g中,以后直接从g中取即可
    
# 3 g和request对象都在当次请求中有效
	-我们一般不直接把数据放到request对象中
    -因为可能会污染数据
# 4 django中没有g
from flask import Flask, g, request

app = Flask(__name__)

app.debug = True
app.secret_key = 'asdfasdf'


@app.before_request
def before():
    if 'index' in request.full_path:
        g.name = 'index'
        request.name='index'
    else:
        g.name = '其他'
        request.name = '其他'
def add():
    print(g.name)
    print(request.name)

@app.route('/index')
def index():
    print(g.name)
    add()
    return 'hello'


@app.route('/home')
def home():
    print(g.name)
    return 'home'


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

3.1 g和session区别

  • g 只在当前请求中有效

  • session可以跨 请求

  • 闪现可以跨请求--》本质就是session--》用一次就没了

4 蓝图(blueprint)

4.1不使用蓝图,自己分文件

  • 目录结构:
-templates
-views
	-__init__.py
    -user.py
    -order.py
-app.py
  • app.py
from views import app
if __name__ == '__main__':
    app.run()
  • init.py
from flask import Flask,request
app = Flask(__name__)
#不导入这个不行
from . import account
from . import order
from . import user
  • user.py
from . import app
@app.route('/user')
def user():
    return 'user'
  • order.py
from . import app
@app.route('/order')
def order():
    return 'order'

4.2 使用蓝图之中小型系统

详见代码:pro_flask_简单应用程序目录示例.zip

  • 目录结构:
-flask_pro
	-flask_test
    	-__init__.py
    	-static
        -templates
        -views
        	-order.py
            -user.py
     -manage.py 
        
  • _init.py
from flask import  Flask
app=Flask(__name__)
from flask_test.views import user
from flask_test.views import order
app.register_blueprint(user.us)
app.register_blueprint(order.ord)
  • manage.py
from flask_test import  app
if __name__ == '__main__':
    app.run(port=8008)
  • user.py
from flask import Blueprint
us=Blueprint('user',__name__)

@us.route('/login')
def login():
    return 'login'
  • order.py
from flask import Blueprint
ord=Blueprint('order',__name__)

@ord.route('/test')
def test():
    return 'order test'

4.3使用蓝图之大型系统

详见代码:pro_flask_大型应用目录示例.zip

4.4总结:

  • xxx = Blueprint('account', name,url_prefix='/xxx') :蓝图URL前缀,表示url的前缀,在该蓝图下所有url都加前缀
  • xxx = Blueprint('account', name,url_prefix='/xxx',template_folder='tpls'):给当前蓝图单独使用templates,向上查找,当前找不到,会找总templates
  • 蓝图的befort_request,对当前蓝图有效
  • 大型项目,可以模拟出类似于django中app的概念

5 flask-session

# 1 第三方 flask-session,可以把session的内容保存在服务端
	-redis
    -数据库
    -文件。。。
    
# 2 安装并使用
pip3 install flask-session

5.1 方式一

from flask_session.redis import RedisSessionInterface
import redis
app = Flask(__name__)
app.secret_key='adsfasdfads'
conn=redis.Redis(host='127.0.0.1',port=6379)
# 1 client:redis链接对象
# 2 key_prefix:放到redis中得前缀
# 3 use_signer:是否使用secret_key 加密
# 4 permanent:关闭浏览器,cookie是否失效
# 5 生成session_key的长度
app.session_interface=RedisSessionInterface(app,client=conn,key_prefix='session',use_signer=True, permanent=True, sid_length=32)

@app.route('/index')
def index():
    session['name']='xiaoming'
    return 'hello'

5.2 方式二,推荐方式

from flask import Flask,session
from flask_session import Session
from redis import Redis
app = Flask(__name__)
app.secret_key='asdfasdf'
app.debug=True


# 配置信息,可以写在 配置文件中
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1', port='6379')
# app.config['SESSION_KEY_PREFIX'] = 'lqz'  # 如果不写,默认以:SESSION_COOKIE_NAME 作为key
# app.config.from_pyfile('./settings.py')

Session(app)  # 核心跟第一种方式一模一样

6 wtforms

# django--->forms组件
	-1 校验数据
    -2 错误处理
    -3 渲染页面
    
    
# flask--》第三方的wtforms

3.1 py

from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets

app = Flask(__name__, template_folder='templates')

app.debug = True


class LoginForm(Form):
    # 字段(内部包含正则表达式)
    name = simple.StringField(
        label='用户名',
        validators=[
            validators.DataRequired(message='用户名不能为空.'),
            validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
        ],
        widget=widgets.TextInput(), # 页面上显示的插件
        render_kw={'class': 'form-control'}

    )
    # 字段(内部包含正则表达式)
    pwd = simple.PasswordField(
        label='密码',
        validators=[
            validators.DataRequired(message='密码不能为空.'),
            validators.Length(min=8, message='用户名长度必须大于%(min)d'),
            validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
                              message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )



@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        form = LoginForm()
        return render_template('login.html', form=form)
    else:
        form = LoginForm(formdata=request.form)
        if form.validate():
            print('用户提交数据通过格式验证,提交的值为:', form.data)
        else:
            print(form.errors)
        return render_template('login.html', form=form)

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

3.2 html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post">
    <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
    <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
    <input type="submit" value="提交">
</form>
</body>
</html>

7 数据库连接池

7.1 flask操作mysql

from flask import Flask, jsonify
import pymysql

app = Flask(__name__)
app.debug=True



##############  使用全局的链接对象  ---》数据安全问题######################
# 1 拿到mysql链接对象
# 分析--》cursor 和conn是全局的--》在并发情况下有问题
# conn = pymysql.connect(
#     user='root',
#     password="1234",
#     host='127.0.0.1',
#     database='blog',
#     port=3306,
#     autocommit=False)
# cursor = conn.cursor(pymysql.cursors.DictCursor)
#
#
# @app.route('/')
# def index():
#     sql = 'select * from blog_tag where id >%s'
#     cursor.execute(sql, 1)
#     res = cursor.fetchall()
#     print(res)
#     return jsonify(res)
#
# @app.route('/home')
# def home():
#     sql = 'select * from blog_commit where id >%s'
#     cursor.execute(sql, 1)
#     res = cursor.fetchall()
#     print(res)
#     return jsonify(res)





##############  每个视图函数中创建一个链接对象  数据库链接数过多#####################
@app.route('/')
def index():
    conn = pymysql.connect(
        user='root',
        password="1234",
        host='127.0.0.1',
        database='blog',
        port=3306,
        autocommit=False)
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from blog_tag where id >%s'
    cursor.execute(sql, 1)
    res = cursor.fetchall()
    print(res)
    return jsonify(res)

@app.route('/home')
def home():
    conn = pymysql.connect(
        user='root',
        password="1234",
        host='127.0.0.1',
        database='blog',
        port=3306,
        autocommit=False)
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from blog_commit where id >%s'
    cursor.execute(sql, 1)
    res = cursor.fetchall()
    print(res)
    return jsonify(res)


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

7.2 数据库连接池

#1  先创建出一批链接,每个请求从池中取链接操作
	-每个请求用自己的链接对象
    -又有池的存在
    -数据并发安全,并且链接数不会过高
    
    
#2 dbutils模块,实现数据库连接池
### 1 安装 pip install dbutils
####2 使用:实例化得到一个池对象---》池是单例
from dbutils.pooled_db import PooledDB
import pymysql
POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=3,
    # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
  host='127.0.0.1',
    port=3307,
    user='root',
    password='123456',
    database='book_xu',
    charset='utf8'
)
## 3 在视图函数中导入使用
##############  数据库连接池#####################
@app.route('/')
def index():
    conn = POOL.connection()
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from book where id >%s'
    cursor.execute(sql, 1)
    res = cursor.fetchall()
    print(res)
    return jsonify(res)

7.3 测试(使用池和不用池)

from flask import Flask, jsonify
from pool import POOL
import pymysql
import time

app = Flask(__name__)
app.debug = True


##############  数据库连接池#####################
@app.route('/tag')
def index():
    conn = POOL.connection()
    # 创建了一个能够将查询结果以字典形式返回的游标对象
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from book where id >%s'
    cursor.execute(sql, 1)
    time.sleep(1)
    res = cursor.fetchall()
    print(res)
    conn.close()
    return jsonify(res)


##############  没有使用连接池#####################
@app.route('/tag_no')
def tag_no():
    conn = pymysql.connect(
        user='root',
        password="123456",
        host='127.0.0.1',
        database='book_xu',
        port=3307,
        autocommit=False)
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    sql = 'select * from book where id >%s'
    cursor.execute(sql, 1)
    time.sleep(1)
    res = cursor.fetchall()
    print(res)
    conn.close()
    return jsonify(res)


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

7.4创建线程任务

import requests
from threading import Thread


# 没有连接池
def task():
    # res = requests.get('http://127.0.0.1:5000/tag_no') # 没有池
    res = requests.get('http://127.0.0.1:5000/tag') #有池
    print(res.json())


if __name__ == '__main__':
    l = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        l.append(t)

    for i in l:
        i.join()


'''
## 效果是:
	使用池的连接数明显小
    不使用池连接数明显很大
# 查看数据库连接数
show status like '%Threads%';
# 查看  Threads_connected 参数

# 使用了池明显比不用池慢
    -因为池太小了---》每个链接耗费时间久---》同一时刻只能有6个在执行--》速度慢

'''


image-20240614161559982

8 flask定制命令

8.1 使用 flask-script定制命令(老版本,不用了)

# flask 老版本中,没有命令运行项目,自定制命令

# flask-script 解决了这个问题:flask项目可以通过命令运行,可以定制命令  1.x  2.x

# 新版的flask--》官方支持定制命令  click 定制命令,这个模块就弃用了  2.x 3.x 



# flask-migrate 老版本基于flask-script,新版本基于flask-click写的

### 使用步骤
	-1 pip3 install  Flask-Script==2.0.3
    -2 pip3 install flask==1.1.4
    -3 pip3 install markupsafe=1.1.1
	-4 使用
    from flask_script import Manager
    manager = Manager(app)
    if __name__ == '__main__':
    	manager.run()
    -5 自定制命令
    @manager.command
    def custom(arg):
        """自定义命令
        python manage.py custom 123
        """
        print(arg)
        
    - 6 执行自定制命令
    python manage.py custom 123

8.2 新版本定制命令

from flask import Flask
import click
app = Flask(__name__)


@app.cli.command("create-user")
@click.argument("name")
def create_user(name):
    # from pool import POOL
    # conn=POOL.connection()
    # cursor=conn.cursor()
    # cursor.excute('insert into user (username,password) values (%s,%s)',args=[name,'123456'])
    # conn.commit()
    print(name)





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


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

# 运行项目的命令是:flask --app py文件名字:app run



# 命令行中执行
# flask --app 7-flask命令:app create-user lqz
# 简写成 前提条件是 app所在的py文件名字叫 app.py
# flask create-user lqz

8.3定制excel命令并插入到数据库中

import os

from flask import Flask
from flask.cli import AppGroup
import click
import pymysql
from openpyxl import load_workbook

app = Flask(__name__)


@app.cli.group()
def excel():
    """Excel related commands."""
    pass


@excel.command("import")
@click.argument("excel_path", type=click.Path(exists=True))
def create_user(excel_path):
    """Import data from Excel to the 'user' table."""
    try:
        # 数据库连接配置
        db_config = {
            "host": "127.0.0.1",
            "user": "root",
            "password": "123456",
            "db": "flask_base",
            "port": 3307,
        }

        # 连接数据库
        with pymysql.connect(**db_config) as connection:
            with connection.cursor() as cursor:
                # 加载Excel文件
                if not os.path.isfile(excel_path) or not excel_path.endswith(('.xlsx', '.xls')):
                    click.echo("Invalid Excel file path or format.")
                    return

                wb = load_workbook(excel_path)
                sheet = wb.active

                # 假设Excel的第一行为表头,从第一行开始为数据
                for row in sheet.iter_rows(min_row=1, values_only=True):
                    name, email, ctime = row  # 根据实际Excel结构调整
                    sql = "INSERT INTO user (name, email, ctime) VALUES (%s, %s, %s)"
                    cursor.execute(sql, (name, email, ctime))

                # 提交事务
                connection.commit()
                click.echo("Data imported successfully.")
    except Exception as e:
        click.echo(f"An error occurred: {e}")


if __name__ == "__main__":
    app.run()
#执行  flask --app app.py excel import .\user.xlsx    

image-20240614172402432

image-20240614172502135

8.3 django中自定制命令

# 1 app下新建文件夹
	management和commands
# 2 在该文件夹下新建py文件,随便命名(命令名)

# 3 在py文件中写代码
from django.core.management.base import BaseCommand
class Command(BaseCommand):
    help = '命令提示'

    def add_arguments(self, parser):
        parser.add_argument('path', nargs='*', type=str,)

    def handle(self, *args, **kwargs):
        print('开始导入')
        print(args)
        print(kwargs)
# 4 使用命令
python manage.py  py文件(命令名)
image-20240614200222788

9flask-cache

https://flask.palletsprojects.com/en/3.0.x/patterns/caching/

# pip3 install Flask-Caching

from flask import Flask
from flask_caching import Cache,SimpleCache

config = {
    "DEBUG": True,  # some Flask specific configs
    "CACHE_TYPE": "SimpleCache",  # Flask-Caching related configs ,可以缓存到redis
    "CACHE_DEFAULT_TIMEOUT": 300
}
app = Flask(__name__)
app.config.from_mapping(config)
cache = Cache(app)


@app.route('/')
def index():
    cache.set('name', 'xxx')
    return 'index'


@app.route('/get')
def get():
    res=cache.get('name')
    return res


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

9.1缓存到redis里面

from flask import Flask
from flask_caching import Cache,SimpleCache

config = {
    "CACHE_REDIS_URL": "redis://localhost:6379/0",  # Redis服务器的URL和数据库号,默认端口6379,数据库0
    "DEBUG": True,  # some Flask specific configs
    "CACHE_TYPE": "redis",  # Flask-Caching related configs ,可以缓存到redis
    "CACHE_DEFAULT_TIMEOUT": 300
}
# 初始化Flask应用
app = Flask(__name__)
app.config.from_mapping(config)

# 初始化缓存
cache = Cache(app)

@app.route('/')
def index():
    cache.set('name', 'hope')
    return 'index'

#取值
@app.route('/get')
def get():
    res=cache.get('name')
    return res


if __name__ == '__main__':
    app.run()
image-20240614184807330 image-20240614184735767

10 更多

# 1 跨域           flask-cors
# 2 jwt            flask-jwt
# 3 后台管理admin   flask-admin
# 4 前后端分离resful flask-resful

11信号

11.1 信号是什么

# 1 Flask框架中的信号基于blinker,其主要就是让开发者可以在flask请求过程中定制一些用户行为
# 2 信号是典型的 观察者模式
	-触发某个事执行【模板准备渲染】
    -绑定信号:可以绑定多个
    	只要模板准备渲染--》就会执行这几个绑定的新--》函数
        
        
# 3 面向切面编程(AOP)--》一种方案
	-整个程序正常运行,但是我们可以把一部分代码,插入到某个位置执行
    -钩子函数:只要写了,程序走到哪,就会执行,没写,就不会执行
    	-序列化类的校验
	
# 4 通过信号可以做什么事?
	-在框架整个执行过程中,插入一些代码执行
    	比如:记录某个页面的访问量
    	比如:每次渲染 login.html --->都记录日志
        比如:程序出异常---》记录日志
        比如:用户表中有个用户创建--》给这个用户发点短信
        比如:用户下了订单---》发个邮件通知,让它尽快付款
        
        比如:轮播图表只要发生变化,就删缓存:django中内置信号

11.2 flask中内置信号的使用

###1 flask中内置信号
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')      # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)
 
appcontext_pushed = _signals.signal('appcontext-pushed')            # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped')            # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed')                # 调用flask在其中添加数据时,自动触发


###2 绑定内置信号,当程序执行到信号位置,就执行我们的函数



### 3 信号和请求扩展的关系
	-有的信号可以完成之前在请求扩展中完成的事
    -但他们机制不一样
    -信号更丰富
from flask import Flask,render_template,signals
app = Flask(__name__)
app.debug=True
###### 内置信号使用---》当模板渲染前[index.html]--》记录日志
# 1 写一个函数
def func1(*args,**kwargs):
    print('模板渲染了')
    print(args)
    print(kwargs.get('template').name)
    if 'index.html' == kwargs.get('template').name:
        print('记日志了')
    # from jinja2.environment import Template
# 2 跟内置信号绑定
signals.before_render_template.connect(func1)
# 3 等待触发(自动)


@app.route('/<string:name>')
def index(name):
    return render_template('index.html',name=name)

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

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

11.3 自定义信号

# 步骤
# 0 定义一个自定义信号
# 1 写一个函数

# 2 跟内置信号绑定

# 3 等待触发(手动)-->只要blog_tag 插入一条记录,就触发
from flask import Flask, render_template, request
from flask.signals import _signals
import pymysql
from pool import POOL
import pymysql

app = Flask(__name__)
app.debug = True
###### 自定义信号
# 0 定义一个自定义信号
create_user = _signals.signal('create_user')


# 1 写一个函数
def func1(*args, **kwargs):
    print('自定义信号执行了')
    if kwargs.get('table_name') == 'blog_tag':
        print('记录日志,blog_tag增加了')
        print(args)
        print(kwargs)


# 2 跟内置信号绑定
create_user.connect(func1)


# 3 等待触发(手动)-->只要blog_tag 插入一条记录,就触发
def insert_data(sql, table_name, *args):
    create_user.send(table_name=table_name)
    conn = POOL.connection()
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute(sql, args)
    conn.commit()


@app.route('/create_tag')
def create_tag():
    name = request.args.get('name')
    blog_id = request.args.get('blog_id')
    sql = 'insert into blog_tag (name ,blog_id) values (%s,%s)'
    insert_data(sql, 'blog_tag', name, blog_id)

    return '创blog_tag成功'


@app.route('/create_category')
def create_category():
    name = request.args.get('name')
    blog_id = request.args.get('blog_id')
    sql = 'insert into blog_category (name ,blog_id) values (%s,%s)'
    insert_data(sql, 'blog_category', name, blog_id)

    return '创blog_tag成功'


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

11.4 django中信号使用

11.4.1Django内置信号

  • 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       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发
Django 提供了一系列的内建信号,允许用户的代码获得DJango的特定操作的通知。这包含一些有用的通知:
django.db.models.signals.pre_save & django.db.models.signals.post_save

在模型 save()方法调用之前或之后发送。
django.db.models.signals.pre_delete & django.db.models.signals.post_delete

在模型delete()方法或查询集的delete() 方法调用之前或之后发送。
django.db.models.signals.m2m_changed

模型上的 ManyToManyField 修改时发送。
django.core.signals.request_started & django.core.signals.request_finished

Django建立或关闭HTTP 请求时发送。

11.4.2内置信号的使用

对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

方式1:

from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created

方式一:

#放到__init__里
from django.db.models.signals import pre_save
import logging
def callBack(sender, **kwargs):
    print(sender)
    print(kwargs)
    # 创建对象写日志
    logging.basicConfig(level=logging.DEBUG)
    # logging.error('%s创建了一个%s对象'%(sender._meta.db_table,kwargs.get('instance').title))
    logging.debug('%s创建了一个%s对象'%(sender._meta.model_name,kwargs.get('instance').title))

pre_save.connect(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)

11.4.3 自定义信号

a. 定义信号(一般创建一个py文件)(toppings,size 是接受的参数)

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

b. 注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
  
pizza_done.connect(callback)

c. 触发信号

from 路径 import pizza_done
  
pizza_done.send(sender='seven',toppings=123, size=456)

由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。

11.5 用信号的好处

# 代码侵入性低---》解耦

11.6 信号和信号量

# 信号:signal 
	-flask,django中得 观察者模式  --》信号机制
    
# 信号量:Semaphore
	-并发编程中概念
    在Python中,信号量(Semaphore)主要用来控制多个线程或进程对共享资源的访问。信号量本质上是一种计数器的锁,它维护一个许可(permit)数量,每次 acquire() 函数被调用时,如果还有剩余的许可,则减少一个,并允许执行;如果没有剩余许可,则阻塞当前线程直到其他线程释放信号量

    
    
    
    
 
    
posted @ 2024-06-27 19:07  -半城烟雨  阅读(5)  评论(0编辑  收藏  举报