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  # 真正的同步进去
posted @ 2023-09-05 14:49  秋洛尘  阅读(24)  评论(0)    收藏  举报