1 flask-session
# 原生的session 把数据加密后放到了cookie中
# 如何将session值 放进数据库中,redis,文件中...
两种方式:
1.指定session类接口:自定义session类 或者 flask-session的 RedisSessionInterface
2.第三方session库: flask-session 配置 (通用方案)
# 方式一:指定session类接口
# 先自定义session类 有:save_session 和 open_session---》操作redis存储和获取
conn = redis.Redis()
# 指定操作session的类--自定义session类 或 第三方 现成的redis-session类
app.session_interface = RedisSessionInterface(conn, 'lqz') # 参数:redis连接对象,redis中key值的前缀
# 方式二:本质和方式一 一样,看源码
通用方案:flask集成第三方库的通用方案 基本也是实例化第三方的类,再包装下Flask对象
# 安装:pip install flask-session
# 区别:使用第三方以后,用法跟之前一样,只是在项目启动是,执行一些代码
from flask_session import Session
# 配置文件 (直接写 实际要写在配置文件的类中设置)
app.config['SESSION_TYPE'] = 'redis' # 可以指定存储到其他地方
app.config['SESSION_KEY_PREFIX'] = 'lqz' # redis中key值的前缀
app.config['SESSION_REDIS'] = redis.Redis()
Session(app)
# 源码在这---》内部就是做了 app.session_interface=RedisSessionInterface(app.config['SESSION_REDIS'])
# 设置session的过期时间,在配置文件中设置
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(seconds=7)
# RedisSessionInterface源码分析
-save_session
-open_session
2 数据库连接池dbutils
# 直接使用pymysql 操作数据库时,需要链接池dbutils
# 为什么要有数据库连接池
-随着并发量的越来越大,mysql的连接数也会增大
-我们创建出连接池后,每次从池中获取连接使用,能够避免并发量过大,导致数据库崩掉的危险
# 使用DBUtils
DBUtils是Python的一个用于实现数据库连接池的模块
# 安装: pip install dbutils
import pymysql
from dbutils.pooled_db import PooledDB
# 获得一个数据库连接池对象 实际生产中:链接池 需要做成单例模型 (放进py文件中,作为模块导入)
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=3306,
user='root',
password='111',
database='cnblogs',
charset='utf8'
)
# 从池中拿出连接
def func():
conn = POOL.connection() # 从链接池中 获取一个连接对象
cursor = conn.cursor()
cursor.execute('select * from user')
result = cursor.fetchall()
print(result)
conn.close()
if __name__ == '__main__':
func()
3 wtfroms(了解)
# 等同于django的forms,表单验证、模板渲染
# 安装:pip install wtfroms
# 能看懂案例,复制修改即可
3.1 登录案例
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__)
app.debug = True
# 写了个form类
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()
<!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>
3.2 注册案例
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
app = Flask(__name__, template_folder='templates')
app.debug = True
class RegisterForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired()
],
widget=widgets.TextInput(),
render_kw={'class': 'form-control'},
default='egon'
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
pwd_confirm = simple.PasswordField(
label='重复密码',
validators=[
validators.DataRequired(message='重复密码不能为空.'),
validators.EqualTo('pwd', message="两次密码输入不一致")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
gender = core.RadioField(
label='性别',
choices=(
(1, '男'),
(2, '女'),
),
coerce=int # “1” “2”
)
city = core.SelectField(
label='城市',
choices=(
('bj', '北京'),
('sh', '上海'),
)
)
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
favor = core.SelectMultipleField(
label='喜好',
choices=(
(1, '篮球'),
(2, '足球'),
),
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput(),
coerce=int,
default=[1, 2]
)
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
def validate_pwd_confirm(self, field):
"""
自定义pwd_confirm字段规则,例:与pwd字段是否一致
:param field:
:return:
"""
# 最开始初始化时,self.data中已经有所有的值
if field.data != self.data['pwd']:
# raise validators.ValidationError("密码不一致") # 继续后续验证
raise validators.StopValidation("密码不一致") # 不再继续后续验证
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
form = RegisterForm(data={'gender': 2,'hobby':[1,]}) # initial
return render_template('register.html', form=form)
else:
form = RegisterForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0 50px">
{% for field in form %}
<p>{{field.label}}: {{field}} {{field.errors[0] }}</p>
{% endfor %}
<input type="submit" value="提交">
</form>
</body>
</html>
4 信号(重要)
# 信号
在视图任务的某些关键位置,绑定上信号函数,视图任务运行到该位置时,会触发执行
# 好处:
eg: 数据库新增成功后,都记录一条日志
原方法:需要在视图里每个操作完数据库之后,手动记录日志
使用信号:只需要定义一个函数,信号在关键位置,都可以记录日志
# 注意:
信号 和 并发编程中 信号量 不是同一个东西!!!
# flask内置信号 from flask import signals
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在其中添加数据时,自动触发
4.1 内置信号使用
# 内置信号执行使用步骤:
# eg:
模板渲染后信号执行
功能:home页面被渲染后,记录一条日志
from flask import signals
# 第一步:写一个函数
def template_test(*args,**kwargs):
print(args)
print(kwargs)
print('模板渲染完了')
# 第二步:注册到内置信号:template_rendered
signals.template_rendered.connect(template_test)
# 第三步:触发信号执行(不需要咱们操作)
4.2 自定义信号
from flask import signals
# 第一步:自定义一个信号
xxxxx = signals._signals.signal('xxxxx')
# 第二步:写一个函数
def xxx_test(*args, **kwargs):
print(args)
print(kwargs)
print('xxx信号触发了')
# 第二步:注册到自定义信号:xxxxx
xxxxx.connect(xxx_test)
# 第三步:触发信号执行 (手动触发,在某个位置触发,比如视图函数中)
@app.route('/')
def index():
xxxxx.send('123123', k1='v1') # 触发信号
return 'hello'
5 flask-script 自定义命令
# 实现类似于这样的命令:python manage.py runserver
##### flask 自定义命令
pip3 install flask-script
-自带一个runserver 命令
-支持自定制命令
# 自定制命令 (数据库初始化,超级用户的创建)
-eg:
启动celery: 脚本文件里:使用subprocess('系统cmd或shell命令') 执行系统命令
清空某个表的所有记录 # ptmysql操作数据库
自动发布项目 # 执行一些 系统命令
-作用:自己写了一些 比如项目准备的小脚本,eg: 创建库、数据库测试数据等函数
然后用 自定制命令的形式 执行脚本
python manage.py 命令函数 参数
# eg:
把excel的数据导入数据库 定制个命令 去执行
##### django 自定义命令
详见:https://pythondjango.cn/django/advanced/11-django-admin-commands/
app01/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py # 以下划线开头文件不能用作管理命令
initdb.py # 这个就是自定义的管理命令脚本,文件名即为命令名
tests.py
views.py
# 步骤
1.在app内创建一个management的包
2.在management目录里面创建commands的包
3.在commands文件夹下创建任意py文件
# 一般建议每个python脚本文件对应一条管理命令
4.命令文件内部
from django.core.management.base import BaseCommand
class Command(BaseCommand):
# 帮助文本, 一般备注命令的用途及如何使用。
help = 'Some help texts'
# 处理命令行参数,可选
def add_arguments(self, parser):
pass
# 核心业务逻辑
def handle(self, *args, **options):
pass
# 执行自定义命令
python manage.py initdb xx.xsl article
# 只要执行这个命令,就向数据库的article表中写入xx.xsl的数据
5.1 基本使用
from flask import Flask
# 第一步 导入
from flask_script import Manager
app = Flask(__name__)
# 第二步 实例化Manger对象
manager = Manager(app)
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
# 第三步 使用
manager.run()
# 此时不支持 文件-右键执行了
# 采用命令行的形式运行
python 文件名 runserver
# 文件名 改为:manage.py
就变成 python manage.py runserver
5.2 自定义命令
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
# 自定义命令
@manager.command
def custom(arg):
"""
自定义命令:python manage.py custom 123
"""
print(arg)
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令(-n也可以写成--name)
执行: python manage.py cmd -n lqz -u http://www.oldboyedu.com
执行: python manage.py cmd --name lqz --url http://www.oldboyedu.com
"""
print(name, url)
if __name__ == '__main__':
manager.run()
6 flask_sqlalchemy使用
# flask集成sqlalchemy模块 操作orm
# 方式一:
采用scoped_session() ,全部导入sqlalchemy模板 来直接书写
# 方式二:
采用第三方模块 flask_sqlalchemy
# flask_sqlalchemy使用步骤
# setting.py 配置文件中 数据库的配置信息需要添加进去
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:@127.0.0.1:3306/db1?charset=utf8"
SQLALCHEMY_POOL_SIZE = 5
SQLALCHEMY_POOL_TIMEOUT = 30
SQLALCHEMY_POOL_RECYCLE = -1
# 追踪对象的修改并且发送信号
SQLALCHEMY_TRACK_MODIFICATIONS = False
# __init__.py中
1.导入from flask_sqlalchemy import SQLAlchemy
2.实例化得到db对象
db = SQLALchemy() # 以后session和表的基类等所有的 都是在db对象中获取
3.在app中注册
db.init_app(app)
# models.py中
4.表模型继承 db.Model
# views中
5.session是db.session
db.session使用即可
# 存在问题
1 表迁移麻烦
2 不支持字段的动态修改
7 flask-migrate使用
# 像djagno一样,执行两条迁移命令,实现数据库的动态迁移
# 安装
pip install flask-migrate # 该模块是基于 flask-script 自定义脚本命令的形式
# 使用步骤
1.导入
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
2.执行
manager = Manager(app) # 先实例化 Manager类
Migrate(app, db)
manager.add_command('db', MigrateCommand) # 用Manager类注册自定义命令
3.命令行执行命令
python manage.py db init # 初始化:只执行一次,会生成一个migrations文件夹
# 以后直接新建表,新建/修改字段,执行命令,就会自动同步
python manage.py db migrate # 等同于 makemigartions
python manage.py db upgrade # 等同于 migrate
# 它是如何实现的呢?
在migrations-->versions目录里面,有一个xx.py,它记录的models.py的修改
那么它和django也是同样,有一个文件记录变化