flask----day06( sqlalchemy快速插入数据 、scoped_session 保证线程安全、 基本增删查改操作 、 和高级查询 、多对多 、 连表查询 、 flask-sqlalchemy使用,注意事项!!! )
.
.
.
.
.
.
今日内容
# 模型表
# 第一步 导入模块
from sqlalchemy import create_engine
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
# 第二步 执行declarative_base,得到一个基类
Base = declarative_base()
# 第三步 继承生成的Base类
class User(Base):
# 第四步 写字段并指定类型
id = Column(Integer, primary_key=True) # Column是生成一列的关键字
# varchar类型32长度,该字段添加索引,nullable=False 不允许为空
name = Column(String(32), index=True, nullable=False)
email = Column(String(32), unique=True)
# datetime.datetime.now # 不能加括号,加了括号以后永远是当前时间
ctime = Column(DateTime, default=datetime.datetime.now)
extra = Column(Text, nullable=True) # 文本类型不能为空
# 第五步 写表名 如果不写以类名为表名
__tablename__ = 'users'
# 第六步 建立联合索引,建立联合唯一
__table_args__ = (
UniqueConstraint('id', 'name', name='uix_id_name'),
Index('ix_id_name', 'name', 'email', )
# uix_id_name 就是联合唯一的名字 ix_id_name 就是联合索引的名字
)
# 直接打印对象时触发
def __str__(self):
return self.name
# 打印一个容器,容器中如果有一个个对象会触发
def __repr__(self):
return self.name
class Book(Base):
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=False)
__tablename__ = 'books'
# 第七步 把表同步到数据库中
# 还是要先生成引擎
# 不会创建库,只会生成表,会在你配置的库里面生成表
engine = create_engine(
'mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=600, # 600s后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用
)
# 把表同步到数据库命令 (把被Base管理的所有表(继承base的表),都同步到数据库)
# 这样只要右键运行该py文件,就可以直接将表同步到数据库了
Base.metadata.create_all(engine)
.
.
.
.
.
.
.
1 sqlalchemy快速插入数据
# sqlalchemy是什么 orm框架,跟其他web框架没有必然联系,可以独立使用
# 安装,快速使用,执行原生sql
# 创建表和删除表
-不能创建数据库
-不能修改字段(增加,删除)
.
.
.
.
.
.
.
.
sqlalchemy往表里面插入数据
# 使用orm插入数据
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 第一步 生成引擎
engine = create_engine(
'mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=10, # 连接被用10次后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用
)
# 第二步 用sessionmaker类产生一个Session类,传入engin
Session = sessionmaker(bind=engine)
# 注意sqlalchemy这里的session和flask里面session没有半毛钱关系!!!
# 第三步 得到db_session对象(会话对象) 相当于pymysql里面的conn连接对象
# 为了区分变量名起成db_session
db_session = Session()
# 第四步 往表里面增加数据 固定步骤
from src.admin.models import Book
book_obj = Book(name='红楼梦1') # 先产生对象
db_session.add(book_obj)
db_session.commit()
# 关闭会话对象
db_session.close()
# sessionmaker类源码4899行,self.class_ = type(class_.__name__, (class_,), {})
# class_ = Session
# type类是元类可以产生类,
变量名 = type( 类的真实名,由父类名称组成的元组(可以为空),包含属性的字典(名称和值))
# 所以上面的就是产生一个 类名叫Session 继承了Session类 并且没有给创建的类添加任何属性与方法
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 使用scoped_session 保证线程安全
2.1 基本使用
# orm.py
# 使用orm插入数据
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 第一步 生成引擎
engine = create_engine(
'mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=10, # 连接被用10次后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用
)
# 第二步 用sessionmaker类产生一个Session类,传入engin
Session = sessionmaker(bind=engine)
# 注意sqlalchemy这里的session和flask里面session没有半毛钱关系!!!
------------------------------------------------------
from . import order_bp
from src.admin.models import Book
from src.orm import Session
count = 0
# 1 这个是将db_session连接对象放在了视图函数里面生成
@order_bp.route('/home', defaults={'name': 'teng'})
def home(name):
global count
count += 1
# 访问该视图函数,就往book表里插入一条数据
book_obj = Book(name='水浒传-%s' % count)
# db_session 放到视图函数外面还是里面???
# 放外面,全局都用这一个db_session连接对象,并发情况下会出现数据错乱情况的
# 放在视图函数里面,每次请求使用一个新的db_session连接对象
db_session = Session()
db_session.add(book_obj)
db_session.commit()
print(count)
return 'haha'
------------------------------------
count1 = 0
from sqlalchemy.orm import scoped_session
# db_session = Session() # 线程不安全,用scoped_session类包一下
# 这样产生的连接对象,就可以放在全局用了
db_session = scoped_session(Session)
# 2 这个是用的scoped_session类产生的全局db_session连接对象
@order_bp.route('/index', defaults={'name': 'teng'})
def index(name):
global count1
count1 += 1
# 访问该视图函数,就往book表里插入一条数据
book_obj = Book(name='三国演义-%s' % count1)
# 放外面,全局都用这一个db_session连接对象,并发情况下会出现数据错乱情况的
# 又想将db_session连接对象放全局,又想不发送并发安全,怎么办
# sqlalchemy使用scoped_session来解决
db_session.add(book_obj)
db_session.commit()
print(count1)
return 'haha'
-----------------------------------------------
# 如何做成线程安全的 ?
scoped_session类191行
self.registry = ThreadLocalRegistry(session_factory)
ThreadLocalRegistry类里面的672行
self.registry = threading.local()
# 内部使用了threading模块里面local对象,
# local对象为每一个线程建立一组键值对,值对应一个小字典,小字典里放该线程放进去的数据!!!
# 取当前线程的session,如果当前线程有,就直接返回用,
# 如果没有,创建一个,放到local对象中
db_session = scoped_session(Session)
# db_session不是Session类的对象了,但是它有Session类对象的所有方法!!!
# db_session 是 scoped_session 的对象
scoped_session类上面有个装饰器
@create_proxy_methods(Session,
":class:`_orm.Session`",
":class:`_orm.scoping.scoped_session`",
classmethods=[...],
methods=[...],
attributes=[...],
)
# 把methods与attributes里面对应的属性都注册到了scoped_session类里面去了!!!
# 以后全局使用db_session即可,它线程安全
.
.
.
.
.
.
.
.
3 基本增删查改
# 增,删,改 查 基本查询 和 高级查询
from src.admin.models import User, Book
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from sqlalchemy.sql import text
engine = create_engine('mysql+pymysql://root:222@127.0.0.1:3306/20230113bbs?charset=utf8',
max_overflow=0, # 超过连接池大小后,还可以再创建的连接数
pool_size=10, # 连接池大小,sqlalchemy自带连接池!!!!!
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=10, # 连接被用10次后,对该连接的回收,再创一个新的连接,-1表示线程不回收一直用)
)
Session = sessionmaker(bind=engine)
db_session = scoped_session(Session)
----------------------------------------
# 1 增加:add add_all
book_obj1 = Book(name='nimadan1')
book_obj2 = Book(name='nimadan2')
user_obj = User(name='lqz', email='22@qq.com', extra='哈哈哈')
# db_session.add(book_obj) 添加一条数据
# book表数据与user表数据可以一次性添加!!!
# 批量添加数据,不同表模型的对象,可以一次性添加!!!
db_session.add_all([book_obj1, book_obj2, user_obj])
db_session.commit()
db_session.close()
----------------------------------------------------------------
----------------------------------------------------------------
# 2 基本查 filter filter_by filer:写条件 filter_by:等于的值
# 2.1 db_session.query(User) 中写表模型名 相当于 select * from User;
# 2.2 filter 过滤条件,必须写 表达式 == >= != > 等
# 2.3 all() 的结果是普通列表里面有查出来的所有对象 first()的结果是单个对象
res = db_session.query(User).filter(User.name == 'lqz') # 还没有真正的执行,只是生成一个sql语句
print(res) # 执行结果是打印sql语句
# filter或filter_by过滤之后,要点all或者first才会执行sql语句
# 点first()会拿到对象, 只会拿查到的所有的对象中的第一个
user = db_session.query(User).filter(User.name == 'lqz').first()
print(user.email)
# 点all()会拿到查到的所有对象 放在一个普通的列表中!!!
user1 = db_session.query(User).filter(User.id > 1).all()[0]
print(user1.email)
# filter_by 直接写等式 不能写成 User.name = 'lqz'
user = db_session.query(User).filter_by(name='lqz').first()
print(user.email)
user1 = db_session.query(User).filter_by(id=2).first()
print(user1.email)
-----------------------------------------------------------
# 3 删除(查到才能删)
filter或filter_by查询的结果 不要all或first出来,直接 .delete() 即可
res = db_session.query(User).filter_by(id=2).delete()
db_session.commit() # 一定不要忘了
print(res) # 影响的行数
-----------------------------------------------------------
# 4 修改(要先查到,才能改)
# 方式一:update修改
res = db_session.query(User).filter_by(id=3).update({"name": "彭于晏1"})
res1 = db_session.query(User).filter_by(id=3).update({User.name: "彭于晏2"})
# 批量修改怎么办? 过滤条件查出来的是多个对象,该orm语句就是批量修改操作!!!
res2 = db_session.query(User).filter_by(id=3).update({User.name: User.name + '888'}, synchronize_session=False )
# synchronize_session=False 的作用就是执行不同步操作,不写默认就是False,
# 尤其是删除操作有时会报错,就需要加这句话了
# 也可以让该字段数据的数字在原有的基础上加对应的数字,比如年龄加5岁
res3 = db_session.query(User).filter_by(id=3).update({User.age: User.age + 5}, synchronize_session='evaluate')
db_session.commit() # 最后id为3的记录里name字段对应的名字改为了: 彭于晏2888
-----------------------------------
# 方式二,使用对象修改 适用于批量修改的场景 先all查出所有对象出来,再for循环出来,一个个的改
res = db_session.query(User).filter_by(id=3).first()
res.name='zzz'
db_session.add(res) # add的对象 如果有主键,就是修改,如果没有主键就是新增
# 上面这句话不要,直接执行下面这句话好像也行!!! 对象改完属性直接commit好像也行!!!
db_session.commit()
.
.
.
.
.
.
.
.
.
.
.
3.1 基本增删查改和高级查询
# 4 查询: filer:写条件 filter_by:等于的值
# 4.1 查询所有 是list对象
res = db_session.query(User).all() # 结果是个普通列表
print(type(res))
print(len(res))
----------------------------------------------
# 4.1.1 可以只查询 表里面的某几个字段
select name as xx,email from user # 原生sql对应的orm语句
res = db_session.query(User.name.label('xx'), User.email).all()
print(res) # [('laoliu', '33@qq.com'), ('lqz', '22@qq.com')] 列表套元祖的形式
res = db_session.query(User.name.label('xx'), User.email)
print(res) # 不点all,会打出原生sql语句出来
----------------------------------------------
# 4.1.2 filter 括号里面写的是表达式,而且字段名前要制定表名
res = db_session.query(User).filter(User.name == "lqz").all()
# 多个条件默认是and
res = db_session.query(User).filter(User.name != "lqz", User.email == '3@qq.com').all()
# filter_by 括号里面写的就是字段对应的值即可,扩展性没filter高,写条件不方便
res = db_session.query(User).filter_by(name='lqz099').all()
print(len(res))
----------------------------------------------
# 4.2 db_session.query(User)生成的是query对象,可以直接点first()
# query对象点all后是python中普通的列表,列表没有first方法
res = db_session.query(User).first()
----------------------------------------------
# 4.3 使用占位符查询 :value :name
select * from user where id <2 or name=lqz # 原生sql
res = db_session.query(User).filter(text("id<:value or name=:name")).params(value=2, name='lqz').all()
-----------------------------------------------
# 4.4 自定义查询(了解)
# from_statement 写纯原生sql语句查询
select * from user where email='3@qq.com' # 原生sql
res=db_session.query(User).from_statement(text("SELECT * FROM users where email=:email")).params(email='3@qq.com').all()
print(res)
-----------------------------------------------
.
.
.
.
.
.
.
filter 高级查询
# 4.5 高级查询 条件表达式
# and条件 连接
res = db_session.query(User).filter(User.id > 1, User.name == 'lqz099').all()
# between
res = db_session.query(User).filter(User.id.between(1, 9), User.name == 'lqz').all()
# in_ 应该是为了避免和关键字冲突加了个杠
res = db_session.query(User).filter(User.id.in_([1,3,4])).all()
res = db_session.query(User).filter(User.email.in_(['3@qq.com','r@qq.com'])).all()
# ~ 非
res = db_session.query(User).filter(~User.id.in_([1,3,4])).all()
print(res)
--------------------------------------------
# 二次筛选 类似于sql里面的子查询 一个查询的结果作为另一个查询的条件
res =db_session.query(User).filter(
User.id.in_( db_session.query(User.id).filter_by(name='lqz') )
).all()
print(res)
# and or条件 or_包裹的都是or条件
from sqlalchemy import and_, or_
res = db_session.query(User).filter(User.id < 3, User.name == 'lqz099').all() # and条件
res = db_session.query(User).filter(or_(User.id < 2, User.name == 'eric')).all() # or条件
res = db_session.query(User).filter(
or_( User.id < 2,
and_(User.name == 'lqz099',User.id > 3),User.extra != "")
)
.all()
# 通配符, 查询email中含有@符的所有数据
res = db_session.query(User).filter( User.email.like('%@%') ).all()
# 以e开头
select user.id from user where user.name like e%; # 原生sql语句
res = db_session.query(User.id).filter(User.name.like('e%'))
.
.
.
.
.
.
.
.
分页与排序查询
# 分页
# 一页2条,查第5页
res = db_session.query(User)[2*5:2*5+2] # 就是拿 从第10条到第12条数据
# 排序
res = db_session.query(Book).order_by(Book.price.desc()).all()
res = db_session.query(Book).order_by(Book.price.asc()).all()
# 第一个条件重复后,再按第二个条件升序排
res = db_session.query(User).order_by(User.name.desc(), User.id.asc())
.
.
.
.
.
.
分组查询
# 分组查询 5个聚合函数
from sqlalchemy.sql import func
res = db_session.query(User).group_by(User.extra) # 按extra字段分组
# 如果是严格模式,该orm语句会错,严格模式下,只能查分组的字段和聚合函数字段
# 分组之后取 分组的字段 最大id id之 和 最小id
res = db_session.query(
User.extra,
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.extra).all()
for item in res:
print(item[2])
------------------------------------------------------
# 分组后再过滤
# having
select max(id),sum(id),min(id) from user group by user.extra having id_max>2;
res = db_session.query(
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.extra).having(func.max(User.id) > 2)
.
.
.
.
.
.
.
.
.
.
.
.
3.2 原生sql
### 方式一:
# 第一步:导入
from sqlalchemy import create_engine
# 第二步:生成引擎对象
engine = create_engine(
"mysql+pymysql://root@127.0.0.1:3306/cnblogs",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第三步:使用引擎获取连接,操作数据库
conn = engine.raw_connection() # 这句话获取连接,应该放在视图内部
cursor=conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select * from aritcle')
print(cursor.fetchall())
-------------------------------------------------------
### 方式二:
from models import User, Book
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
db_session = scoped_session(Session)
# 2.0.9 版本需要使用text包裹一下,原来版本不需要
cursor = db_session.execute(text('select * from users'))
result = cursor.fetchall()
print(result)
cursor = db_session.execute(text('insert into books(name) values(:name)'), params={"name": '红楼梦'})
session.commit()
session.close()
.
.
.
.
.
.
.
3.3 django中执行原生sql
# 选择的查询基表Book.objects.raw ,只是一个傀儡,正常查询出哪些字段,都能打印出来
def index(request):
books = Book.objects.raw('select * from app01_book where id=1')
# RawQuerySet 用起来跟列表一样
print(books[0])
print(type(books[0]))
res = Book.objects.raw('select * from app01_publish where id=1')
# RawQuerySet 用起来跟列表一样
print(res[0])
print(type(res[0]))
print(res[0].name)
print(res[0].addr)
return HttpResponse('ok')
.
.
.
.
.
.
.
.
.
.
.
4 一对多
# 一对一:本身是一个表,拆成两个表,做一对一的关联; 本质就是一对多,只不过关联字段唯一
# 一对多:关联字段写在多的一方
# 多对多:需要建立中间表;本质也是一对多
# 本质就只有一种外键关系
.
.
.
4.1 表模型
# 一对多关系
from sqlalchemy import create_engine
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.orm import relationship
# 第二步:执行declarative_base,得到一个类
Base = declarative_base()
class Hobby(Base):
__tablename__ = 'hobby'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='篮球')
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(32), index=True, nullable=True)
# hobby指的是tablename而不是类名
# 关联字段写在多的一方,写在Person中,跟hobby表中id字段做外键关联
hobby_id = Column(Integer, ForeignKey("hobby.id"))
# 该虚拟的hobby字段,跟数据库无关,不会新增字段,只用于快速链表操作
# 基于对象的跨表查询: 就要通过这个虚拟字段,才能跳到关联的表
# person对象.hobby得到关联的对象
# pserson对象.hobby_id 只会拿到hobby_id字段对应的值,不会调到关联表里面去,和django有点小区别
# 类名,backref用于反向查询,比如如果有hobby对象,hobby.pers 拿到所有关联的人对象了!!!
hobby = relationship('Hobby', backref='pers')
def __repr__(self):
return self.name
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa", )
# 把表同步到数据库 (把被Base管理的所有表,都创建到数据库)
Base.metadata.create_all(engine)
# 把所有表删除
# Base.metadata.drop_all(engine)
.
.
.
.
.
.
.
.
4.2 新增和基于对象的查询
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from models1 import Hobby, Person
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
db_session = scoped_session(Session)
------------------------------------------------------------
# 一对多关系 新增
hobby=db_session.query(Hobby).filter(Hobby.caption=='乒乓球').first()
db_session.add(hobby)
person = Person(name='王五',hobby_id=1)
db_session.add(person)
db_session.commit()
------------------------------
# 支持按对象的增加方式,必须加relationship 做关联
# 方式一
hobby=db_session.query(Hobby).filter(Hobby.caption=='乒乓球').first()
person = Person(name='赵六',hobby=hobby) # 一旦按对象添加,前面就要用虚拟字段了
# 方式二
hobby = Hobby(caption='羽毛球') # 产生一条数据对象,还没提交,表中暂时没有
person = Person(name='赵六', hobby=hobby) # 产生一条数据对象,还没提交,表中暂时没有
db_session.add_all([person, hobby])
db_session.commit() # 一起提交
--------------------------------------------------------------
## 基于对象的跨表查询
# 正向查询
person=db_session.query(Person).filter(Person.name=='王五').first()
print(person.hobby) # 通过person对象点虚拟的hobby字段,得到外键关联的hobby对象
print(person.hobby_id) # 得到的是person对象对应的hobby_id字段对应的实际值,不会跳到关联表去
# 反向查询
hobby=db_session.query(Hobby).filter(Hobby.id==1).first()
print(hobby.pers) # 列表里面放对应的所有的,多的对象
for i in hobby.pers:
print(i.name)
# 还有一种假设要查Hobby表对应id==1 关联的Person表里的所有对应的多的对象
直接就查出来了 person_all_obj = db_session.query(Person).filter(hobby_id==1).all()
--------------------------------------------------------------
# 基于连表的查询(一会讲)
.
.
.
.
.
.
.
.
.
5 多对多
5.1 表模型
# 多对多关系
from sqlalchemy import create_engine
import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, UniqueConstraint, Index
from sqlalchemy.orm import relationship
# 第二步:执行declarative_base,得到一个类
Base = declarative_base()
# 中间表 手动创建
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)
name = Column(String(64), unique=True, nullable=False)
# 与生成表结构无关,仅用于查询方便,放在哪个单表中都可以
# 方便快速查询,写了这个字段,相当于django 的manytomany,快速使用基于对象的跨表查询
girls = relationship('Girl', secondary='boy2girl', backref='boys')
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa", )
# 把表同步到数据库 (把被Base管理的所有表,都创建到数据库)
Base.metadata.create_all(engine)
# 把所有表删除
# Base.metadata.drop_all(engine)
.
.
.
.
.
.
.
.
5.2 增加和基于对象的跨表查询 多对多关系
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from models2 import Girl, Boy, Boy2Girl
engine = create_engine("mysql+pymysql://root@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
db_session = scoped_session(Session)
-------------------------------------------------
# 新增
# 1 笨办法新增
girl=Girl(name='刘亦菲')
boy=Boy(name='彭于晏')
db_session.add_all([girl,boy])
db_session.add(Boy2Girl(girl_id=1,boy_id=1))
db_session.commit()
# 2 使用relationship
boy = Boy(name='lqz')
boy.girls = [Girl(name='迪丽热巴'), Girl(name='景田')]
db_session.add(boy)
db_session.commit()
-------------------------------------------------
# 基于对象的跨表查询
# 正向
boy = session.query(Boy).filter(Boy.id==2).first()
print(boy.girls)
# 反向
girl = session.query(Girl).filter(Girl.id==2).first()
print(girl.boys)
# 如果没有relationship,纯自己操作 怎么纯自己操作? 看下老刘视频
# 基于连表的查询(一会讲)
.
.
.
.
.
.
.
6 连表查询
# 一对多关联关系,基于连表的跨表查询
from models3 import Person,Hobby
# 不建议使用的连表方式!!!
res = db_session.query(Person, Hobby).filter(Person.hobby_id == Hobby.id).all()
select * from person,hobby where person.hobby_id=hobby.id; # 对应的原生sql语句
# 连表操作,这种通过笛卡尔积的方式让person与hobby里面的数据相乘,
# 很多乘很多时,就可能会直接卡死!!!所以用笛卡尔积的方式连表并不合理!!
------------------------------------------------------------
------------------------------------------------------------
# 自己连表查询
# join, 默认是inner join, 自动按外键关联 join括号里面要放,要连接的表的表名
# 别放成起始表的表名!!!
select * from Person inner join Hobby on Person.hobby_id=Hobby.id; # 原生sql语句
res = db_session.query(Person).join(Hobby,).all() # 自动按外键关联,有外键关联才能这样写
res = db_session.query(Person).join(Hobby,Person.hobby_id==Hobby.id).all() # 一样的
# isouter=True 外连, 没有右连接,反过来即可
select * from Person left join Hobby on Person.hobby_id=Hobby.id; # 原生sql语句
res = db_session.query(Person).join(Hobby, isouter=True).all() # 表示Person左连接Hobby表
res = db_session.query(Person).outerjoin(Hobby,).all() # 也可以这样写
# outerjoin相当于LEFT OUTER JOIN 左外连接;outerjoin( ) 返回结果有null
# 注意query括号里面放表名,代表的是要查询该表里的所有字段!!!!
# 还可以这样写,指定起始表名,一般在多张表连接的时候,习惯这样写,能够让结构更清晰!!!
res = db_session.query(Person).select_from(Person).outerjoin(Hobby,Person.hobby_id=Hobby.id).all()
# 没有right join,通过这个实现
res = db_session.query(Hobby).join(Person, isouter=True).all()
res = db_session.query(Hobby).outerjoin(Person,).all() # 或这样
------------------------------------------------------------
# 自己指定on条件(连表条件),第二个参数,支持on多个条件,用and_,同上
select * from Person left join Hobby on Person.id=Hobby.id; # 原生sql语句
res = db_session.query(Person).join(Hobby, Person.hobby_id == Hobby.id, isouter=True)
# sql本身有问题,只是举个例子
print(res)
------------------------------------------------------------
------------------------------------------------------------
# 示例
# 内连接
res1 = db_session.query(User1.name, Age1.age).join(Age1, User1.age_id == Age1.id).all()
res11 = db_session.query(User1.name, Age1.age).join(Age1, User1.age_id == Age1.id)
print(res1) # [('lihua', 55), ('lqz', 56), ('haha', 57), ('haha2', 58)]
print(res11) # SELECT user1.name AS user1_name, age1.age AS age1_age FROM user1 INNER JOIN age1 ON user1.age_id = age1.id
# 左连接
res2 = db_session.query(User1.name, Age1.age).outerjoin(Age1, User1.age_id == Age1.id).all()
res22 = db_session.query(User1.name, Age1.age).outerjoin(Age1, User1.age_id == Age1.id)
print(res2) # [('lihua', 55), ('lqz', 56), ('jason', 57), ('jack', 58), ('john', None), ('jocker', None)]
print(res22) # SELECT user1.name AS user1_name, age1.age AS age1_age FROM user1 LEFT OUTER JOIN age1 ON user1.age_id = age1.id
# 左连接
res3 = db_session.query(User1.name, Age1.age).join(Age1, User1.age_id == Age1.id, isouter=True).all()
res33 = db_session.query(User1.name, Age1.age).join(Age1, User1.age_id == Age1.id, isouter=True)
print(res3) # [('lihua', 55), ('lqz', 56), ('jason', 57), ('jack', 58), ('john', None), ('jocker', None)]
print(res33) # SELECT user1.name AS user1_name, age1.age AS age1_age FROM user1 LEFT OUTER JOIN age1 ON user1.age_id = age1.id
--------------------------------------------------------------
.
.
.
.
.
.
.
.
.
.
多对多关系连表查询
# 多对多关系连表查询
#方式一:直接连 一般不这样用,查询效率很低
res = db_session.query(Boy, Girl,Boy2Girl).filter(Boy.id == Boy2Girl.boy_id,Girl.id == Boy2Girl.girl_id).all()
# 方式二:join连表 方便高效
res = db_session.query(Boy).join(Boy2Girl).join(Girl).filter(Boy.id>=2).all()
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6 flask-sqlalchemy模块的使用
# 有个第三方flask-sqlalchemy,帮助咱们快速的集成到flask中
# 使用flask-sqlalchemy模块的步骤
from flask_sqlalchemy import SQLAlchemy # 1 导入
db = SQLAlchemy() # 2 # SQLAlchemy类实例化生成db对象
# 后续我们去操作数据库的时候 db.session 生成的
# 就是原来的我们用scoped_session(Session类)生成的连接对象db_session
db.init_app(app) # 3 将db注册到app中 一般写在蓝图注册代码的下面
# 上面几几句话,一般写在项目的包文件下的__init__文件里 从创建app对象、生成db对象、注册蓝图对象、
# 最后将db对象注册到app对象中去,这样项目包下的__init__文件差不多也就写完了
# 4 视图函数中使用数据库连接对象,
# 直接导入__init__的db对象,用db.session 就拿到了连接对象了!!!
全局的db.session # 线程安全的
# 5 models.py 中模型类要继承Model
# 原来用sqlalchemy的时候模型类继承的是Base Base=declarative_base() c产生的
class User(db.Model):
username = db.Column(db.String(80), unique=True, nullable=False)
# 6 写字段,需要用db.Column() 原来sqlalchemy是直接 Column()
username = db.Column(db.String(80), unique=True, nullable=False)
# 7 配置文件中配置下连接地址与数据库连接池参数,就是我们之前生成的引擎对象的参数
# 这个样在生成db对象的时候,就已经把引擎的参数绑定进去了
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root@127.0.0.1:3306/ddd?charset=utf8"
SQLALCHEMY_POOL_SIZE = 100 # 设置连接池的大小,别设太小
SQLALCHEMY_POOL_TIMEOUT = 600 # 池中没有连接,最多等待时间,超过还没拿到就报错!!!
SQLALCHEMY_POOL_RECYCLE = 1800 # -1 表示不回收 1800秒后回收掉该连接对象
# 追踪对象的修改并且发送信号
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 池数量设的小,超时等待时间也设定小,容易触发超时等待的报错,导致后端崩掉!!!
------------------------------------------------------
.
.
flask-sqlalchemy 使用代码示范
src目录下的init文件代码
from flask import Flask, request, current_app
from flask_cors import CORS
# 引入Flask-SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # 实例化SQLAlchemy
# 引入蓝图
from .blueprints import auth
from .blueprints import organization
# 引入模型
from app_flask_service.models.flasksqlalchemy_models_app import *
import settings
import logging
import logging.handlers
# 创建app
def create_app():
app = Flask(__name__)
CORS(app, resources=r'/*') #解决跨域的
app.debug = False
app.secret_key = 'shdtw' # 自定义的session秘钥
# 设置配置文件
product_cfg = settings.ProductionConfig()
app.config.from_object(product_cfg)
# 定义日志记录器
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('app')
logger.setLevel(logging.INFO)
# 配置 logger 写入 rotating日志文件
file_handler = logging.handlers.RotatingFileHandler('./logs/log_app.log', maxBytes=1024 * 1024 * 10, backupCount=5)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
logger.addHandler(file_handler)
# 注册蓝图
app.register_blueprint(auth.blueprint, url_prefix='/auth')
app.register_blueprint(organization.blueprint, url_prefix='/organization')
db.init_app(app)
return app
-----------------------------------------
settings配置文件
TOKEN_EXPIRE_TIME = 3600 # token生成时设置的过期时间(秒)
APP_DB_CONF = {
'DB_type': 'mysql',
'DBAPI': 'pymysql',
'IP_address': '192.168.11.99', # 本机:'localhost', 生产环境:'58.40.119.146',
'port': '3306', # 本机:'3306', 生产环境:'xxxx',
'username': 'root',
'pwd': 'root123',
'path': 'shctwl_qr_jingan'
}
## 基础信息配置信息 -->
def db_uri(config):
return "{db_type}+{dbapi}://{username}:{pwd}@{ip_address}:{port}/{path}".format(
db_type=config['DB_type'],
dbapi=config['DBAPI'],
username=config['username'],
pwd=config['pwd'],
ip_address=config['IP_address'],
port=config['port'],
path=config['path']
)
class BaseConfig(object):
JSON_AS_ASCII = False
JSON_SORT_KEYS = False
JSONIFY_MIMETYPE = "application/json;charset=utf-8"
SQLALCHEMY_DATABASE_URI = db_uri(APP_DB_CONF) + "?charset=utf8" # 数据库用户名:密码@host:port/数据库名?编码
SQLALCHEMY_TRACK_MODIFICATIONS = False # 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。
# SQLALCHEMY_ECHO = True
'''session绑定redis'''
'''
SESSION_TYPE = 'redis' # session类型为redis
SESSION_KEY_PREFIX = 'session:' # 保存到session中的值的前缀
SESSION_PERMANENT = False # 如果设置为True,则关闭浏览器session就失效。
SESSION_USE_SIGNER = False # 是否对发送到浏览器上 session:cookie值进行加密
'''
class ProductionConfig(BaseConfig):
SQLALCHEMY_POOL_SIZE = 100
SQLALCHEMY_POOL_TIMEOUT = 600
SQLALCHEMY_POOL_RECYCLE = 800
SQLALCHEMY_MAX_OVERFLOW = 50 # 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 100,
'pool_timeout': 600,
'pool_recycle': 800,
'max_overflow': 50,
'pool_pre_ping': True
}
class DevelopmentConfig(BaseConfig):
SQLALCHEMY_POOL_SIZE = 50
SQLALCHEMY_POOL_TIMEOUT = 300
SQLALCHEMY_POOL_RECYCLE = -1
SQLALCHEMY_MAX_OVERFLOW = 20 # 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。
class TestingConfig(BaseConfig):
SQLALCHEMY_POOL_SIZE = 50
SQLALCHEMY_POOL_TIMEOUT = 300
SQLALCHEMY_POOL_RECYCLE = -1
SQLALCHEMY_MAX_OVERFLOW = 20 # 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。
----------------------------------------
# 启动文件 service_server.py
import os, sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from gevent import monkey
monkey.patch_all()
from app_flask_service import *
from utils.uwsgi_server.server_gevent import * # 见下面的文件
# from utils.uwsgi_server.server_flask_debug import *
app = create_app()
if __name__ == "__main__":
print('垃圾品质识别系统接口服务启动……')
server = uwsgi_server(app=app, address='0.0.0.0', port=5000)
# server = uwsgi_server(app=app, address='0.0.0.0', port=39090)
server.start()
----------------------------------------------
# server_gevent.py文件
import os,sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from gevent.pywsgi import WSGIServer
import urllib3
urllib3.disable_warnings()
class uwsgi_server():
def __init__(self,app,address='0.0.0.0',port=5000) -> None:
self.server = WSGIServer((address, port), app)
def start(self):
self.server.serve_forever()
.
.
.
使用flask-sqlalchemy模块的时候注意事项!!!
# 一般把创app的代码都封装到create_app方法中
# 使用flask_sqlalchemy的时候,如果不在视图函数操作数据库或者使用Flask的全局变量,
# 需要先
app.app_context():
把操作数据库的代码写在这儿
# 不然会报错!!! 说什么不在请求上下文里面什么的!!!
.
.
.
.
.
.
.
.
.
.
7 flask-migrate使用
# 表发生变化,都会有记录,自动同步到数据库中
# 原生的sqlalchemy,不支持修改表的
# flask-migrate可以实现类似于django的
python manage.py makemigrations # 记录
python manage.py migrate # 真正的同步到数据库
-----------------------------------------------------------
# 使用步骤
flask:2.2.2 flask-script:2.0.3
第一步:安装,依赖于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)
Migrate(app, 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 # 真正的同步进去
.
.
.
.
.
.
.
.
.
.
.
.
加在类上的装饰器
def speak():
print('说话了')
def wrapper(func):
def inner(*args, **kwargs):
res = func()
res.name = 'lqz'
res.speak = speak
return res
return inner
@wrapper # 语法糖会把Person当参数传入到装饰器中 Person=wrapper(Person)
class Person:
pass
p = Person()
print(p.name) # 通过加在类上的装饰器,给类实例化出来的对象,添加新的属性与方法
p.speak()
.
.
.
.
sqlalchemy 的orm语法里面的filter与where的区别
在SQLAlchemy的ORM语法中,`filter`和`where`都用于添加条件来筛选查询结果,但它们在使用上有一些区别。
1. `filter`方法:`filter`是ORM语法中常用的方法,它用于添加查询条件。
它接受一个或多个表达式作为参数,每个表达式表示一个筛选条件。这些条件将使用逻辑与(AND)进行组合,
并在生成的SQL查询中作为WHERE子句的一部分。
示例:
# 使用filter添加多个条件
query = session.query(User).filter(User.username == 'John', User.age >= 30)
# 使用filter添加单个条件
query = session.query(User).filter(User.username.like('%Smith%'))
`filter`方法可以连续调用,它们将逐步添加条件,最终生成一个组合了所有筛选条件的查询。
-----------------------------------
2. `where`方法:`where`方法用于在查询中添加原始的SQL WHERE子句。它接受一个SQLAlchemy的文本表达式(`text`)作为参数,该表达式表示完整的WHERE子句。`where`方法更适合于需要使用自定义SQL条件的高级查询场景。
示例:
# 使用where添加自定义的SQL WHERE子句
query = session.query(User).where(text("username = 'John' AND age >= 30"))
`where`方法允许您直接传递SQL语句的片段作为条件,并且不会进行任何自动参数绑定或表达式解析。
总体而言,`filter`方法更常用且更易于使用,它允许您以更Pythonic的方式构建查询条件,而无需直接编写SQL语句。而`where`方法更适用于需要更高级、自定义SQL条件的场景,它提供了更大的灵活性和控制权。
-----------------------------------
.
.
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了