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>
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个在执行--》速度慢
'''
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
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文件(命令名)
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()
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() 函数被调用时,如果还有剩余的许可,则减少一个,并允许执行;如果没有剩余许可,则阻塞当前线程直到其他线程释放信号量