flask
flask 介绍
# python 界的web框架
-Django:大而全,快速开发,公司内部项目
-Flask:小而精,不具备web开发好多功能,丰富的第三方插件
-FastApi:异步框架,主要为了做前后端分离接口
-Sanic:异步框架,只支持python3.6 及以上,性能比较高
-Tornado:公司用的比较少。。。
# Flask 框架
- pip3 install flask
fastapi简单使用
# 下载
- pip install fastapi
- pip install uvicorn
# 使用
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def index():
return 'hello word!!'
# 启动
uvicorn 配置文件名字:FastAPI()得到的对象名字 --reload
flask使用
# 文件命名不能与关键字相同
from flask import Flask
# 类实例化得到对象,我们一般会传一个字符串__name__进去
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return 'hello world'
# 启动
if __name__ == '__main__':
app.run()
wsgiref
# 服务 wsgi协议的web服务器,django的web服务用的就是它
# 相当于个socket服务端,可以接收客户端发送过来的请求,处理,返回给客户端
from wsgiref.simple_server import make_server
def mya(environ, start_response):
print(environ)
start_response('200 OK', [('Content-Type', 'text/html')])
if environ.get('PATH_INFO') == '/index':
with open('index.html','rb') as f:
data=f.read()
elif environ.get('PATH_INFO') == '/login':
with open('login.html', 'rb') as f:
data = f.read()
else:
data=b'<h1>Hello, web!</h1>'
return [data]
if __name__ == '__main__':
myserver = make_server('', 8011, mya)# 监听本地的8011端口,当请求来了,就会执行 mya(),传入两个参数,一个是environ:http请求转成python的字典,一个是start_response:响应对象
print('监听8010')
myserver.serve_forever()
Werkzeug
Werkzeug是一个WSGI工具包(在它基础上,继续封装),他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
显示用户小案例
from flask import Flask, render_template, request, redirect, session
import functools
USERS = {
1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"},
2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"},
3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"},
}
app = Flask(__name__, template_folder='templates')
app.secret_key = 'sdfsdfsdfsdf'
app.debug = True
# 在Flask中多个函数如果想要使用同一个装饰器,需要在装饰器中加入@wraps(),这个方法利用的是装饰器的修复技术,返回的结果是每个函数的名字。如果不加 functools 这个装饰器的话,每个函数都是 inner 用来区分每个 inner
def auth_login(func):
@functools.wraps(func)
def inner(*args, **kwargs):
if not session.get('username'):
return redirect('/login')
res = func(*args, **kwargs)
return res
return inner
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if username == 'gjl' and password == '123':
session['username'] = username
return redirect('/index')
return render_template('login.html', error='用户名或密码错误')
return render_template('login.html')
@app.route('/index')
@auth_login
def index():
return render_template('index.html', users=USERS)
@app.route('/detail/<int:pk>')
@auth_login
def detail(pk):
user = USERS.get(pk)
return render_template('detail.html', user=user)
if __name__ == '__main__':
app.run()
'''
1 新手三件套: 1 直接返回字符串 2 render_template 3 redirect
2 flask的路由写法,是基于装饰器的 @app.route('/detail/<int:pk>' ,methods=['GET'])
3 路由转换器跟django一样
4 取出前端post请求提交的数据:request.form
5 取出请求方式:request.method
6 使用session设置值和取值
-session[key]=value
-session.get('key')
7 flask的模板语法完全兼容dtl,并且它更强大,可以加括号执行
'''
detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息 {{user.name}}</h1>
<div>
{{user.text}}
</div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in users.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<p><input type="submit" value="提交">{{error}}</p>
</form>
</body>
</html>
装饰器(类)
# 0 装饰器的本质原理
# 类装饰器:1 装饰类的装饰器 2 类作为装饰器
# 装饰器使用位置,顺序
# Flask路由下加装饰器,一定要加endpoint
-如果不指定endpoint,反向解析的名字都是函数名,不加装饰器没有问题,就是正常函数index,detail
-如果加了装饰器---》index,detail都变成了inner---》反向解析的名字都是函数名inner,报错了
-wrapper装饰器----》把它包的更像---》函数名变成了原来函数的函数名
def add(func):
print(func)
# 类装饰器:1 装饰类的装饰器 2 类作为装饰器
# add 一定是个函数吗?
# 放个对象
class Person:
def __call__(self, func):
def inner(*args, **kwargs):
res = func(*args, **kwargs)
return res
return inner
p = Person()
# @add # test=add(test)--->test变成了None
@p # test=p(test) # p() 会触发__call__--->Person的 __call__(func)--->返回inner,以后test就是inner---》test(参数)--》执行的是inner
def test():
print("test")
print(test)
def auth(func):
def inner(*args, **kwargs):
res = func(*args, **kwargs) # 真正的执行视图函数,执行视图函数之,判断是否登录
res.name='lqz'
return res
return inner
@auth # Foo=auth(Foo)
class Foo():
pass
f=Foo() # Foo() 调用 ---》inner()--->类实例化得到对象,返回,以后f就是Foo的对象,但是可以里面多了属性或方法
print(f)
print(f.name)
### 有参装饰器--->额外为被装饰的函数传参数
@auth(10) # Foo=auth(10)(Foo)
class Foo():
pass
配置文件
# 所有web框架都会有配置文件,只是配置的形式不一样,如Django中的配置文件是settings
# 方式一:
直接设置,不过只能设置 DEBUG和SECRET_KEY
app.debug = True
app.secret_key = 'xxxxxxxx'
# 方式二:
使用config
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'xxx'
# 方式三:
使用配置文件
app.config.from_pyfile("python文件名称")
# 方式四:
使用类
app.config.from_object("python类或类的路径")
app.config.from_object('pro_flask.settings.TestingConfig')
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
路由系统
# flask路由系统是基于装饰器的
@app.route('/detail/<int:nid>', methods=['GET'], endpoint='detail')
# rule:路径 # methods :请求方式,列表 # endpoint: 路径别名
'''
endpoint 不传会怎么样,不传会以视图函数的名字作为值,但是如果加了装饰器,所有视图函数名字都是inner,就会出错,使用wrapper装饰器再装饰装饰器
'''
# 转换器:
''' string int path
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
'''
# 路由系统本质 ---> 源码
'''
def decorator(f: T_route) -> T_route:
endpoint = options.pop("endpoint", None) #从options弹出,如果没有,就是None ---》@app.route(传了就有,不传就是None)
self.add_url_rule(rule, endpoint, f, **options)
return f # f 就是视图函数,没有对视图函数做事情,只是在执行视图函数之前,加了点操作
核心:self.add_url_rule(rule, endpoint, f, **options)---》self就是app对象
app.add_url_rule('路由地址', '路由别名', 视图函数, **options)--->跟django很像
add_url_rule的参数详解
rule, URL规则,路径地址
view_func, 视图函数名称
defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}
为函数提供参数
endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
methods = None, 允许的请求方式,如:["GET", "POST"]
#对URL最后的 / 符号是否严格要求
strict_slashes = None
#重定向到指定地址
redirect_to = None,
'''
from flask import Flask
app = Flask(__name__)
app.config['DEBUG'] = True
def index(name):
print(name)
return 'index'
app.add_url_rule('/index', 'index', index, defaults={'name': 'gjl'}, strict_slashes=None,
redirect_to='https://www.baidu.com')
if __name__ == '__main__':
app.run()
CBV
1 cbv写法
-1 写个类,继承MethodView
-2 在类中写跟请求方式同名的方法
-3 注册路由:app.add_url_rule('/home', view_func=Home.as_view('home')) #home是endpoint,就是路由别名
2 cbv加装饰器
-方式一:
class Home(MethodView):
decorators = [auth] # 之前加载fbv上的,直接拿过来用即可
-方式二:
class Home(MethodView):
@auth # 这个auth需要单独写,跟加载fbv上的不一样
def get(self):
return render_template('home.html')
3 允许的请求方式
class Home(MethodView):
methods = ['GET'] # 控制能够允许的请求方式
4 cbv源码
5 为什么decorators = [auth] 能加装饰器
app.add_url_rule('/home', view_func=view内存地址)
用装饰器一直在装饰 view内存地址 ,所以,以后执行,就是有装饰器的view,装饰器代码会走
@auth
def view(**kwargs):
return self.dispatch_request(**kwargs)
等价于
view = auth(view)
6 view_func=Home.as_view('home') home 是 endpoint,就是路由别名
-1 app.add_url_rule('/home', view_func=Home.as_view('home'))
-2 add_url_rule---》endpoint没传---》会执行endpoint = _endpoint_from_view_func(view_func)
-3 取函数名作为 endpoint 的值
-4 view_func是 加了一堆装饰器的view函数---》它的名字应该是装饰器的名字--》但是
view.__name__ = name # name 就是home
-5 所以endpoint就是你传的home
-6 如果传了endpoint,就是传的那个,那as_view('home')就没作用了,但是也必须要传
# as_view源码
# View的as_view
@classmethod
def as_view(cls, name, *class_args, **class_kwargs) :
def view(**kwargs):
return self.dispatch_request(**kwargs)
if cls.decorators: # 咱们的装饰器
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators: #每次拿出一个装饰器,
view = decorator(view) # 装饰器语法糖干的事: 把被装饰的函数当参数传入到装饰器,返回结果赋值给被装饰的函数,一个个用装饰器包装view
view.__name__ = name
return view
# self.dispatch_request---》MethodView
def dispatch_request(self, **kwargs) :
# 取到request.method.lower()请求方式小写 ---》假设是get请求get
# meth是 cbv中 以get命名的方法,反射出来了
meth = getattr(self, request.method.lower(), None)
return meth(**kwargs) # 执行cbv中跟请求方式同名的方法
模板
# 之前dtl中学的所有知识,拿到这,都可以用---》dtl是django自己的,不能独立使用
# jinja2 模板语法,第三方,flask使用了它,它可以单独使用
# jinja2 模板语法 支持括号调用,支持 字典[] 取值----》模板中写原来python的语法都支持
# jinja2模板语法处理了xss攻击
django,jinja2处理xss攻击原理是?
-使用了html特殊字符的替换,把字符串中得 < > 都用特殊字符替换
# extends,include
请求响应
# 所有web:请求对象,响应对象(go,java,ptyhon)
django:request(每个请求一个request),新手四件套
flask:requset:全局的,但是也是每个请求一个request,新手三件套
# flask请求对象,全局的request
# 请求相关信息
# request.method 请求的方法
# request.args get请求提交的数据
# request.form post请求提交的数据
# request.values post和get提交的数据总和
# request.cookies 客户端所带的cookie
# request.headers 请求头
# request.path 不带域名,请求路径
# request.full_path 不带域名,带参数的请求路径
# request.url 带域名带参数的请求路径
# request.base_url 带域名请求路径
# request.url_root 域名
# request.host_url 域名
# request.host 服务端地址
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))
# flask的响应
-四件套:
-1 直接返回字符串
-2 返回模板:render_template
-3 返回重定向:redirect,
-4返回json格式:jsonify
-响应中写cookie
res = make_response('home')
res.set_cookie('yyy', 'yyy', path='/home')
# 删除cookie
# res.delete_cookie('key')
-响应头中写内容
res = make_response('home') # res 就是响应对象
res.headers['xxx'] = 'xxx'

session
# cookie session token
# flask中得session,没有在服务端存储数据的---》后期扩展,可也把session存到redis中
# 全局session
-放值:session['key']=value
-取值:session.get('key')
-删除值:session.pop('username', None)
# session的运行机制
'''
django
1 生成一个随机字符串
2 把数据保存到djagno-session表中
3 把随机字符串返回给前端--》当cookie存到浏览器中了--》浏览器再发请求,携带cookie过来
4 根据随机字符串去表中查---》转到request.session中
flask
1 把数据加密转成字符串: eyJuYW1lIjoibHF6In0.ZMnbJw.ZUceSaD0kGnU97tu9ZWm3380r00
2 以cookie形式返回给前端---》保存到浏览器中
3 浏览器再发请求,携带cookie过来
4 加密符串---》解密---》放到session对象中
'''
# 源码分析,看运行机制
-flask默认使用:SecureCookieSessionInterface作为session的类
-请求来了,
-客户端带了cookie---》取出cookie 中session对应的值
-使用解密方式对它进行解密
-放到session对象中
-请求走了
-把用户放到session中得数据
-加密---》转成字符串--》以cookie形式返回给前端
-SecureCookieSessionInterface的方法e:
-open_session:请去来了用
-save_session:请求走了用
闪现
# 一个请求---》假设出错了---》重定向到另一个地址---》把错误信息在另一个返回中看到
错误信息放个位置----》另一个请求过来,去那个位置拿
# 把一些数据,放在某个位置---》后期可以去取出来----》取完不用删除,就没了
from flask import Flask, flash, redirect, get_flashed_messages
app = Flask(__name__)
app.secret_key = 'asdfg'
app.debug = True
@app.route('/')
def home():
flash('超时错误', category="x1")
return redirect('/error')
@app.route('/error')
def error():
print(get_flashed_messages(category_filter="x1"))
return 'xxx'
if __name__ == '__main__':
app.run()
# 如何设置
flash('aaa')
# 如何取
get_flashed_message()
# 分类放
flash('超时错误', category="x1")
# 分类取
data = get_flashed_messages(category_filter=['x1'])
请求扩展
# 类似于django中的中间件
-请求来了,请求走了,做一些拦截
1 before_request
2 after_request
3 before_first_request
4 teardown_request
5 errorhandler
6 template_global
7 template_filter
# before_request
# 请求来了执行 ---> 有多个就从上往下依次执行
@app.before_request
def before():
if request.path == '/':
request.name = 'gjl'
else:
return '没有权限访问'
@app.before_request
def before2():
print('456')
@app.route('/')
def home():
print(request.name)
return 'test'
# after_reuqest
# 请求走了执行,要返回一个响应对象 ---> 多个的话是由下往上执行
@app.after_request
def after(response):
print('走了')
return response
@app.after_request
def after1(response):
print('走了啊')
return response
# before_first_request 项目运行第一次执行,新版本已经弃用了
# teardown_request 每一个请求之后绑定一个函数,即使遇到了异常---》一般用来记录日志--->不能做统一异常处理
@app.teardown_request
def ttt(e):
# 通过判断e是否为空,记录日志
print(e) # 出了异常,e就是异常对象,如果没有异常,e就是None
if e:
return '出异常了'
# 返回异常还是会报错
# errorhandler 有参装饰器,将http请求失败状态码传入
@app.errorhandler(404)
def error(e):
print(e)
return jsonify({'code': 999, 'msg': '服务器内部错误,请联系系统管理员'})
# template_global 全局标签,所有页面都可以用
@app.template_global()
def add(a1, a2):
return a1 + a2
# template_filter 全局过滤器
@app.template_filter()
def db(a1, a2, a3):
print(a1)
return a1 + a2 + a3
g对象
# 请求来了,在request中放个path,后续的视图函数中都能拿到
# flask不建议向request对象中放变量,建议使用g对象
# g对象,当次请求中放入的变量,在当次请求中一直存在
# g和session的区别
-g只针对于当次请求
-session可以跨请求
蓝图
# blueprint :蓝图,flask都写在一个文件中,项目这样肯定不行,分目录,分包,使用蓝图划分目录
# 不用蓝图,划分目录
-一直使用app对象,会出现循环导入问题
-项目名
static
templates
order_detail.html
views
__init__.py
goods.py
order.py
user.py
manage.py
-代码看详情
# 使用蓝图:所有步骤
-1 实例化得到一个蓝图对象
-order_blue=Blueprint('order',__name__,template_folder='../templates')
-2 以后注册路由,写请求扩展,都使用蓝图
@user_blue.before_request
@user_blue.route('/register')
-3 在app中注册蓝图
from . import user
app.register_blueprint(user.user_blue)
app.register_blueprint(user.user_blue,url_prefix='/user')

蓝图小型项目
flask_blueprint_little # 项目名
src # 核心文件
__init__.py #包的inin里面实例化得到app对象
views # 视图函数,类
user.py
order.py
templates #模板
user.html
static #静态文件
manage.py #启动文件
蓝图大型项目

flask-session
# flask 自带session---》以cookie的形式放到了浏览器中---》加密
#真正的session,是在服务端存储
-django中存在djangosession表中
-flask中,使用第三方,保存在---》redis中---》flask-session
#flask能不能用jwt
# 使用步骤
pip3 install flask-session
# 降一下flask版本即可
# 用高版本:在app中放一个参数 app.session_cookie_name='session'
# 使用方式一:
from flask_session import RedisSessionInterface
app.session_cookie_name='session'
app.session_interface=RedisSessionInterface(redis=None,key_prefix='lqz') # 动态替换,把原来的session对象换成放到redis的session对象
# 4 以后再使用session,就会存到redis中了
session.get()
session[]=value赋值
# 使用方式二:
from redis import Redis
from flask_session import Session
app.session_cookie_name = 'session'
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
Session(app)

数据库连接池

数据库连接池
# 1 安装:DBUtils
# 2 使用 类创建一个池对象
PYMYSQL_POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=2, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=1, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=0, # 链接池中最多闲置的链接,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='123',
database='cars',
charset='utf8'
)
# 3 从池对象中,取出一个链接使用
conn = PYMYSQL_POOL.connection()
# 4 flask中使用
@app.route('/')
def index():
conn = PYMYSQL_POOL.connection() # 从池中拿一个链接
cursor = conn.cursor(cursor=DictCursor) # 默认元组套元组,设置DictCursor就是列表套字典
cursor.execute('select id,title from news where id<10')
res1 = cursor.fetchall()
cursor.close()
conn.close()
return jsonify(res1)
信号
# Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为
# 内置信号
内置信号的使用
# 第一步:写一个函数
def test(app, **kwargs):
print(app)
print(type(kwargs))
# 请求地址是根路径,才记录日志,其它都不记录
print(kwargs['context']['request'].path)
if kwargs['context']['request'].path == '/':
print('记录日志了')
# 第二步:跟内置信号绑定
# signals 中有很多内置信号
signals.before_render_template.connect(test)
# 第三步:等待信号被触发(不需要咱们做)--->只要执行到内置信号位置,绑定的函数就会执行
# 有哪些内置信号
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在其中添加数据时,自动触发
# 自定义信号
# 1 第一步:定义一个自定义 信号
# 2 第二步:写个函数
# 3 第三步:函数跟自己定义信号绑定
# 4 第四步:触发自定义信号---》我们做
from flask import Flask, session, render_template, signals
from flask.signals import _signals
# pip3 install blinker
app = Flask(__name__)
app.debug = True
app.secret_key = 'asdfasdfasdf'
#### 自定义信号---》session每次放一个值,我们就执行信号
# 1 定义信号
# 自定义信号
session_input = _signals.signal('session_input')
# 2 写个函数
def test2(*args, **kwargs):
print(args) # app
print(kwargs) # {session kk}
print('session放值了')
if kwargs.get('kk').get('name') == 'xx':
print('记录日志')
# 3 绑定信号
session_input.connect(test2)
# 4 触发信号
@app.route('/')
def index():
session['uu'] = '00'
session_input.send(app, session=session, kk={'name': 'uu'})
return render_template('index.html')
@app.route('/home')
def home():
return render_template('home.html')
@app.route('/order')
def order():
session['xx'] = 'xx'
session_input.send(app, session=session, kk={'name': 'xx'})
return "order"
if __name__ == '__main__':
app.run(port=8080)
信号的作用(信号量--》Semaphore)
# 对代码进行解耦
# 1 记录日志:只要是张三用户,访问index页面,就记录日志
# 2 只要用户表中,插入一条记录,就给用户发个短信通知
User.object.create--->调用发短信方法--》找到10个地址--》改10个地方
-如果有个内置信号---》只要表中增加记录,就会触发这个信号----》通过信号内判断这个表是不是User表,决定要不要发短信
-flask中没有这个内置信号---》自定义
# acquire():消耗信号量
# release():释放信号量
import threading
import time
def run(n):
semaphore.acquire() # 计数器获取锁
time.sleep(5) # 程序休眠5秒
print (n)
semaphore.release() # 计数器释放锁
if __name__ == '__main__':
# 添加一个计数器,最大并发线程数量5(最多同时运行5个线程)
semaphore = threading.Semaphore(5)
for i in range(50):
t = threading.Thread(target=run, args=(i, )) # 创建线程
t.start()
django的信号
# 内置信号:
#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 # 创建数据库连接时,自动触发
# 内置信号使用(当user表创建用户,就给用户发个邮件)
1 写个函数 #放到__init__里
from django.db.models.signals import pre_save
import logging
def callBack(sender, **kwargs):
logging.debug('%s创建了一个%s对象'%(sender._meta.model_name,kwargs.get('instance').title))
2 绑定内置信号
pre_save.connect(callBack)
3 等待触发
# 内置信号
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)
# 自定义信号:
#1 定义信号(一般创建一个py文件)(toppings,size 是接受的参数)
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
# 2 注册信号
def callback(sender, **kwargs):
print("callback")
print(sender,kwargs)
pizza_done.connect(callback)
# 3 触发信号
from 路径 import pizza_done
pizza_done.send(sender='seven',toppings=123, size=456)
# 自定义信号干过什么?
-做双写一致性的缓存更新
flask-script
# django中,有命令
python manage.py runserver
python manage.py makemigrations
...自定制命令(django如何自定制命令)...
-python manage.py init_db excel文件路径 指定表名
# flask启动项目,像djagno一样,通过命令启动
Flask==2.2.2
Flask_Script==2.0.3
#借助于:flask-script 实现
-安装:pip3.8 install flask-script
-修改代码:
from flask_script import Manager
manager=Manager(app)
manager.run()
-用命令启动
python38 manage.py runserver
# 自定制命令
#1 简单自定制命令
@manager.command
def custom(arg):
# 命令的代码,比如:初始化数据库, 有个excel表格,使用命令导入到mysql中
print(arg)
#2 复杂一些的自定制命令
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
# python run.py cmd -n lqz -u xxx
# python run.py cmd --name lqz --url uuu
print(name, url)
# django 中如何自定制命令
sqlalchemy介绍和快速使用
# orm 框架----》django orm--》只能用在django中,不能独立使用
# python界的orm框架
-peewee
-sqlalchemy:企业级
-djagno rom
-Tortoise ORM
-GINO
# go 界orm框架
-gorm 国人写的
-Xorm
# java界orm框架
-ssh 框架springmvc structs Hibernate(java的orm框架)
-ssh spring springmvc Hibernate
-ssm Spring SpringMVC MyBatis (orm框架)
-springboot :sb框架 ---》java工程师就是spring工程师
-spring cloud
# 分层:
Engine,框架的引擎
Connection Pooling ,数据库连接池
Dialect,选择连接数据库的DB API种类(sqlite,mysql...)
Schema/Types,架构和类型
SQL Exprression Language,SQL表达式语言
#操作不同数据库
MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html
# 了解
orm不能创建数据库---》只能创建表,删除表---》sqlalchemy不能增加删除字段--》借助于第三方插件实现
sqlalchemy的原生操作
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
# 第一步:创建engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/cars?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:通过engine获得链接
conn=engine.raw_connection()
cursor = conn.cursor()
cursor.execute(
"select * from news"
)
result = cursor.fetchall()
print(result)
cursor.close()
conn.close()
sqlalchemy创建删除表
# models.py 表设计
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.ext.declarative import declarative_base
import datetime
Base = declarative_base() # Base 类
# print(type(Base))
class User(Base):
# 以__开头的是配置
__tablename__ = 'users' # 数据库表名称,如果不写,以类名作为表名
id = Column(Integer, primary_key=True) # 主键索引,聚簇索引
name = Column(String(64), index=True, nullable=False) # name字段加辅助索引
email = Column(String(32), unique=True)
# datetime.datetime.now不能加括号,加了括号,以后永远是当前时间
ctime = Column(DateTime, default=datetime.datetime.now())
extra = Column(Text, nullable=True)
__table_args__ = (
UniqueConstraint('id', 'name', name='uix_id_name'), #联合唯一
Index('ix_id_name', 'name', 'email'), #索引
)
# 创建,删除表
# pip install sqlalchemy
from sqlalchemy import create_engine
from models import Base
# 第一步创建 engine对象
engine = create_engine(
'mysql+pymysql://root:1234@127.0.0.1:3306/db01?charset=utf8',
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 在数据库中创建表 所有被Base所管理的表都会被建立在db01库中
# Base.metadata.create_all(engine)
# 删除表 所有被Base所管理的表都会被删除,若想要只删除单个表,需要自己去手动删除
Base.metadata.drop_all(engine)
# 操作表中得数据
# 第二步:得到一个session对象---》不是flask的session---》会话---》链接
Session = sessionmaker(bind=engine)# 把引擎传入
session = Session() # 得到session对象
#
# # 第三步:使用session对象操作数据
# # 创建对象
user=User(name='lqz',email='33@qq.com')
# # 保存到数据库
session.add(user)
session.commit() # 提交事务
session.close() #关闭会话
scoped_session线程安全
# 如果集成到flask中,session会话是要做成全局,还是每个视图函数有自己的一个
# 应该做成,每个视图函数,都新创建一个session对象
# 这样每次都要加括号得到session对象
# scoped_session 全局只有一个session对象,在不同视图函数就用这一个---》保证线程安全
-做成了每个线程自己一个单独的session对象
# scoped_session 总结:
1 以后scoped_session的对象,就像使用Session的对象一样用---》装饰器放进去了
2 scoped_session 是线程安全的,如何做到的
-每个线程自己的一个session对象
-self.registry = ThreadLocalRegistry(session_factory)
3 t=threading.local() 很神奇,多线程并发操作,不需要加锁,不会出现并发安全问题,每个线程用的都是自己的那个数据
-核心原理是:通过线程id号做个区分
-线程1 t.a=88 内部 ---》{线程id号:{a:88}}
-线程1 t.a=77 内部 ---》{线程id号:{a:88},线程id号:{a:77}}
线程1 t.a=100 --->在当前线程中 print(t.a) ---》100
线程2 t.a=99 --->在当前线程中 print(t.a) ---》99
不同线程用的都是t对象threading.local(),但是每个线程用自己的数据
import sqlalchemy
from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base,User
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:得到一个session对象---》不是flask的session---》会话---》链接
Session = sessionmaker(bind=engine)# 把引擎传入
session = Session() # 得到session对象
print(type(session))
session = scoped_session(Session) # 这个session全局用一个即可 :sqlalchemy.orm.scoping.scoped_session
print(type(session))
#
# # 第三步:使用session对象操作数据
# # 创建对象
user=User(name='lq12z',email='3333@qq.com')
# # 保存到数据库
session.add(user)
session.commit() # 提交事务
session.close() #关闭会话
# 研究:Session 的区别和联系scoped_session
from sqlalchemy.orm.session import Session
from sqlalchemy.orm.scoping import scoped_session
# scoped_session 没有add方法,调用的时候,会有
基本增删查改
from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
from models import Base, User
from sqlalchemy.orm import sessionmaker
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()
# session = scoped_session(Session) # 这个session全局用一个即可 :sqlalchemy.orm.scoping.scoped_session
# 1 增加数据 add add_all
# session.add(User(name="xxx",email='55'))
# user1=User(name="123",email='12')
# user2=User(name="44",email='2323')
# session.add_all([user1,user2])
# 2 查 filter传的是表达式,filter_by传的是参数
# all() 出来的是列表---》不是qs对象,没有这个东西
# 想拿单条 .first()
# select * from User where User.id>2 limit 1;
# res=session.query(User).filter(User.id>2).first()
# res=session.query(User).filter(User.name=='lqz').all()
# res=session.query(User).filter_by(id=3).all()
# res=session.query(User).filter_by(name='lqz').all()
# print(res)
# 删除
# delete * from User where id >6;
# res = session.query(User).filter(User.id >= 6).delete()
# print(res) # 影响的行数
# 不能删除,没有方法
# user=session.query(User).filter(User.id == 5).first()
# user.delete() # 它没有单独删对象的
# 改
# res=session.query(User).filter(User.id > 0).update({"name" : "lqz"})
#类似于django的F查询
# session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False) # 字符串相加
# session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate") # 数字相加
# 查到单个对象,修改属性--》add增加进去---》修改? add 只要有id,就是修改
res=session.query(User).filter(User.id == 1).first()
# print(res)
res.name='yyyy'
session.add(res)
session.commit() # 提交事务
session.close() # 关闭会话
一对多(一对一)
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='篮球')
def __str__(self):
return self.caption
def __repr__(self):
return self.caption
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True) # 不会自动生成id
name = Column(String(32), index=True, nullable=True)
# hobby指的是tablename而不是类名
# 一对多关系一旦确立,关联关系写在多的一方---》物理外键
hobby_id = Column(Integer, ForeignKey("hobby.id"))
# 跟数据库无关,不会新增字段,只用于快速链表操作
# 类名,backref用于反向查询
hobby = relationship('Hobby', backref='pers') # 以后 person.hobby 就是hobby对象
def __str__(self):
return self.name
def __repr__(self):
return self.name
新增和基于对象的查询
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User, Person, Hobby
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()
# 1 增加数据方式一
# session.add(Hobby())
# session.add(Person(name='彭于晏',hobby_id=1))
# session.add(Person(name='彭于晏',hobby_id=2)) # 报错
# 2 增加数据方式二
# session.add(Person(name='彭于晏', hobby=Hobby(caption='足球'))) # 新增hobby和person
# 3 查询
# 通过hobby查询 person 反
hobby = session.query(Hobby).filter_by(id=2).first()
print(hobby)
# 所有喜欢足球的人 relationship('Hobby', backref='pers') 按 backref
print(hobby.pers)
# 4 修改 和删除 跟之前一样
# 通过person查询hobby 正
# p = session.query(Person).filter_by(id=1).first()
# print(p) # 获取彭于晏的爱好---》正向查询按字段
# print(p.hobby_id)
# print(p.hobby)
# print(p.hobby.caption)
session.commit()
session.close()
多对多
表模型
# 多对多关系
# 中间表 手动创建
class Boy2Girl(Base):
__tablename__ = 'boy2girl'
id = Column(Integer, primary_key=True, autoincrement=True)
girl_id = Column(Integer, ForeignKey('girl.id'))
boy_id = Column(Integer, ForeignKey('boy.id'))
class Girl(Base):
__tablename__ = 'girl'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
def __str__(self):
return self.name
def __repr__(self):
return self.name
class Boy(Base):
__tablename__ = 'boy'
id = Column(Integer, primary_key=True, autoincrement=True) #autoincrement 默认就是true
name = Column(String(64), unique=True, nullable=False)
# 就是咱们之前的ManyToMany,不会在表中生成字段---》因为它是个表----》这个字段可以放在Girl表
girls = relationship('Girl', secondary='boy2girl', backref='boys')
def __str__(self):
return self.name
def __repr__(self):
return self.name
增加和基于对象的跨表查询
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from models import Base, User, Person, Hobby,Girl,Boy,Boy2Girl
from sqlalchemy.orm import sessionmaker
# 第一步:创建engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/db001?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine) # 把引擎传入
session = Session()
# 增加记录
# 新增
# 1 笨办法新增
# girl=Girl(name='刘亦菲')
# boy=Boy(name='彭于晏')
# session.add_all([girl,boy])
# 操作中间表(纯手动操作中间表)
# session.add(Boy2Girl(girl_id=1,boy_id=1))
# 2 使用relationship
# boy = Boy(name='lqz') # 增加了一个boy
# boy.girls = [Girl(name='迪丽热巴'), Girl(name='景田')] # 增加了俩girl
# #给这一个boy,增加了两条约会记录
# session.add(boy)
##查询
# 基于对象的跨表查询
# 正向
# boy = session.query(Boy).filter(Boy.id==2).first()
# print(boy.girls)
# 反向
girl = session.query(Girl).filter(Girl.id==2).first()
print(girl.boys)
session.commit()
session.close()
更多查询方式
#1 查询: filer:写条件 filter_by:等于的值
# 查询所有 是list对象
res = session.query(User).all() # 是个普通列表
print(type(res))
print(len(res))
# 2 只查询某几个字段
# select name as xx,email from user;
res = session.query(User.name.label('xx') , User.email)
# print(res) # 打出原生sql
# # print(res.all())
# for item in res.all():
# print(item[0])
# 3 filter传的是表达式,filter_by传的是参数
res = session.query(User).filter(User.name == "lqz").all()
res = session.query(User).filter(User.name != "lqz").all()
res = session.query(User).filter(User.name != "lqz", User.email == '3@qq.com').all()
#4 django 中使用 Q (与 或 非) 如果是, 就是and条件
res = session.query(User).filter_by(name='lqz099').all()
res = session.query(User).filter_by(name='lqz099',email='47@qq.com').all()
# 5 取一个 all了后是list,list 没有first方法
res = session.query(User).first()
# 6查询所有,使用占位符(了解) :value :name
# select * from user where id <20 or name=lqz
res = session.query(User).filter(text("id<:value or name=:name")).params(value=20, name='lqz').all()
# 7 自定义查询(了解)
# from_statement 写纯原生sql
res=session.query(User).from_statement(text("SELECT * FROM users where email=:email")).params(email='3@qq.com').all()
print(type(res[0])) # 是book的对象,但是查的是User表 不要这样写
print(res[0].name) #
# 8 高级查询
# 条件
# 表达式,and条件连接
res = session.query(User).filter(User.id > 1, User.name == 'lqz099').all() # and条件
# between
res = session.query(User).filter(User.id.between(1, 9), User.name == 'lqz099').all()
res = session.query(User).filter(User.id.between(1, 9)).all()
# in
res = session.query(User).filter(User.id.in_([1,3,4])).all()
res = session.query(User).filter(User.email.in_(['3@qq.com','r@qq.com'])).all()
# ~非,除。。外
res = session.query(User).filter(~User.id.in_([1,3,4])).all()
print(res)
# 二次筛选
res = session.query(User).filter(~User.id.in_(session.query(User.id).filter_by(name='lqz'))).all()
print(res)
# and or条件
from sqlalchemy import and_, or_
# or_包裹的都是or条件,and_包裹的都是and条件
res = session.query(User).filter(and_(User.id >= 3, User.name == 'lqz099')).all() # and条件
res = session.query(User).filter(User.id < 3, User.name == 'lqz099').all() # 等同于上面
res = session.query(User).filter(or_(User.id < 2, User.name == 'eric')).all()
res = session.query(User).filter(or_(User.id < 2,and_(User.name == 'lqz099', User.id > 3),User.extra != ""))
# 通配符,以e开头,不以e开头
res = session.query(User).filter(User.email.like('%@%')).all()
# select user.id from user where user.name not like e%;
res = session.query(User.id).filter(~User.name.like('e%'))
res = session.query(User).filter(~User.name.like('e%')).all()
# 分页
# 一页2条,查第5页
res = session.query(User)[2*5:2*5+2]
# 排序,根据name降序排列(从大到小)
res = session.query(User).order_by(User.email.desc()).all()
res = session.query(Book).order_by(Book.price.desc()).all()
res = session.query(Book).order_by(Book.price.asc()).all()
# 第一个条件重复后,再按第二个条件升序排
res = session.query(User).order_by(User.name.desc(), User.id.asc())
# 分组查询 5个聚合函数
from sqlalchemy.sql import func
# 分组后,只能拿分组字段和聚合函数字典,如果拿别的,是严格模式,会报错
res = session.query(User).group_by(User.extra) # 如果是严格模式,就报错
# 分组之后取最大id,id之和,最小id 和分组的字段
from sqlalchemy.sql import func
res = session.query(
User.name,
func.max(User.id),
func.sum(User.id),
func.min(User.id),
func.avg(User.id)).group_by(User.name).all()
for item in res:
print(item)
# 分组后having
# select name,max(id),sum(id),min(id) from user group by user.name having id_max>2;
from sqlalchemy.sql import func
res = session.query(
User.name,
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.name).having(func.max(User.id) > 2).all()
print(res)
连表查询
### 关联关系,基于连表的跨表查询
from models1 import Person,Hobby
# 链表操作
select * from person,hobby where person.hobby_id=hobby.id;
res = session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()
# 自己连表查询
# join表,默认是inner join,自动按外键关联
# select * from Person inner join Hobby on Person.hobby_id=Hobby.id;
# res = session.query(Person).join(Hobby).all()
#isouter=True 外连,表示Person left join Favor,没有右连接,反过来即可
# select * from Person left join Hobby on Person.hobby_id=Hobby.id;
# res = session.query(Person).join(Hobby, isouter=True).all()
# 没有right join,通过这个实现
# res = session.query(Hobby).join(Person, isouter=True).all()
# # 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
# select * from Person left join Hobby on Person.id=Hobby.id;
# res = session.query(Person).join(Hobby, Person.hobby_id == Hobby.id, isouter=True) # sql本身有问题,只是给你讲, 自己指定链接字段
# 右链接
# print(res)
# 多对多关系连表
# 多对多关系,基于链表的跨表查
# 多表链接
#方式一:直接连
#select * FROM boy, girl, boy2girl WHERE boy.id = boy2girl.boy_id AND girl.id = boy2girl.girl_id
# res = session.query(Boy, Girl,Boy2Girl).filter(Boy.id == Boy2Girl.boy_id,Girl.id == Boy2Girl.girl_id)
# 方式二:join连
# SELECT* FROM boy INNER JOIN boy2girl ON boy.id = boy2girl.boy_id INNER JOIN girl ON girl.id = boy2girl.girl_id WHERE boy.id >= %(id_1)s
res = session.query(Boy).join(Boy2Girl).join(Girl).filter(Boy.id>=2)
print(res)
原生sql(django-orm如何执行原生sql)
sqlalchemy执行原生sql
# 有的复杂sql 用orm写不出来---》用原生sql查询
# 原生sql查询,查出的结果是对象
# 原生sql查询,查询结果列表套元组
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/db001", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
#### 执行原生sql方式一:
# 查询方式一:
# cursor = session.execute('select * from users')
# result = cursor.fetchall()
# print(result) #列表套元组
# 添加
# cursor = session.execute('insert into users(name,email) values(:name,:email)',
# params={"name": 'lqz', 'email': '3333@qq.com'})
# session.commit()
# print(cursor.lastrowid)
###执行原生sql方式二(以后都用session操作---》socpe_session线程安全)一般不用
# conn = engine.raw_connection()
# cursor = conn.cursor()
# cursor.execute(
# "select * from app01_book"
# )
# result = cursor.fetchall()
# 执行原生sql方式三:
# res = session.query(User).from_statement(text("SELECT * FROM boy where name=:name")).params(name='lqz').all()
session.close()
django执行原生sql
# 执行完的结果映射到对象中---》上面讲的 方式三:
from model import Book
books_obj_list = Book.objects.raw('select distinct id, book_name from test_book')
for book_obj in books_obj_list:
print(book_obj.id, book_obj.book_name)
# 纯原生sql
from django.db import connection
cur=connection.cursor()
cur.execute('select distinct id, book_name from test_book')
print(cur.fetch_all())
cur.close()
with connection.cursor() as cur:
cur.execute('select distinct id, book_name from test_book')
flask-sqlalchemy使用
# sqlalchemy 集成到flask中
# 第三方: flask-sqlalchemy 封装了用起来,更简洁
# 使用flask-sqlalchemy集成
1 导入 from flask_sqlalchemy import SQLAlchemy
2 实例化得到对象
db = SQLAlchemy()
3 将db注册到app中
db.init_app(app)
4 视图函数中使用session
全局的db.session # 线程安全的
5 models.py 中继承Model
db.Model
6 写字段
username = db.Column(db.String(80), unique=True, nullable=False)
7 配置文件中加入
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root@127.0.0.1:3306/ddd?charset=utf8"
SQLALCHEMY_POOL_SIZE = 5
SQLALCHEMY_POOL_TIMEOUT = 30
SQLALCHEMY_POOL_RECYCLE = -1
# 追踪对象的修改并且发送信号
SQLALCHEMY_TRACK_MODIFICATIONS = Falses
flask-migrate使用
# 表,字段发生变化,都会有记录,自动同步到数据库中--》django支持这种操作
# 原生的sqlalchemy,不支持修改表的
# flask-migrate可以实现类似于django的
python manage.py makemigrations #记录
python manage.py migrate #真正的同步到数据库
# 使用步骤
0 flask:2.2.2 flask-script:2.0.3 flask-migrate==2.7.0
1 第一步:安装,依赖于flask-script
pip3.8 install flask-migrate==2.7.0
2 在app所在的py文件中
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
manager = Manager(app)
# flask-script可以自定义命令---》
# flask-migrate本质是它借助于flask-script增加了几个命令来对数据库表和字段进行管理
Migrate(app, db) # sqlalchemy的db对象
manager.add_command('db', MigrateCommand)
manager.run() # 以后使用python manage.py runserver 启动项目
3 以后第一次执行一下
python manage.py db init # 生成一个migrations文件夹,里面以后不要动,记录迁移的编号
4 以后在models.py 写表,加字段,删字段,改参数
5 只需要执行
python manage.py db migrate # 记录
python manage.py db upgrade # 真正的同步进去

浙公网安备 33010602011771号