flask的sqlalchemy快速插入数据、scoped_session线程安全、基本的增删查改、一对多、多对多、连表查询
今日内容
-
sqlalchemy快速插入数据
-
scoped_session线程安全
-
基本的增删改查
-
一对多
-
多对多
-
连表查询
1 sqlalchemy快速插入数据
# sqlalchemy是什么
ORM框架,跟其他web框架没有必然联系,可以独立使用
# 安装,快速使用,执行原生sql
# 创建表和删除表
-不能创建数据库
-不能修改字段(增加,删除)
# 使用orm插入
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Book
# 第一步:生成engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:拿到一个Session类,传入engine
Session = sessionmaker(bind=engine)
# 第三步:拿到session对象,相当于连接对象(会话)
session = Session()
# 第四步,增加数据
book = Book(name='红楼梦', )
session.add(book)
session.commit()
# 第五步:关闭session对象
session.close()
2 scoped_session线程安全
2.1 基本使用
from flask import Flask
from models import User
app = Flask(__name__)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 第一步:生成engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:拿到一个Session类,传入engine
Session = sessionmaker(bind=engine)
@app.route('/index')
def index():
# 访问一下,就往users表中插入一条记录
user = User(name='superxz', email='123@qq.com')
# session 放到视图函数外面或里面 哪个对?
# 外面 全局都用这一个session连接会话,数据错乱---》flask中,以后就用全局的session,不用每次都新建一个session,会有并发安全问题
# 第三步:拿到session对象,相当于连接对象(会话)
session = Session()
session.add(user)
session.commit()
session.close()
return '增加成功'
if __name__ == '__main__':
app.run()
因为全局都用一个session对象,会有并发安全问题,于是sqlalchemy就是用了scoped_session解决了这个问题
from models import User
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 第一步:生成engine对象
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/aaa",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 第二步:拿到一个Session类,传入engine
Session = sessionmaker(bind=engine)
from threading import Thread
from sqlalchemy.orm import scoped_session
# 线程不安全
# session = Session()
# 做成线程安全的,如何做?
# 内部使用了local对象,取当前线程的session,日过当前线程有,就直接返回用,如果没有,创建一个,放到local中
# session 是 scoped_session 的对象
session = scoped_session(Session)
# session对象,不是Session的对象,但它有Session的所有方法
def task():
user = User(name='qwe')
session.add(user)
session.commit()
session.close()
return '增加成功'
if __name__ == '__main__':
for i in range(10):
t = Thread(target=task)
t.start()
做成线程安全的:如何做的?
内部使用了local对象,取当前线程的session,如果当前线程有,就直接返回用,如果没有,创建一个,放到local中
session 是 scoped_session 的对象
session = scoped_session(Session)
以后全局使用session即可,它线程安全
2.2 加在类上的装饰器
# session 是 scoped_session 的对象,类上没有属性和方法,但是,用的时候,确实用
session = scoped_session(Session)
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()
3 基本增删查改
# 增,删,改
# 查 基本查询和高级查询
from 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@127.0.0.1:3306/aaa")
Session = sessionmaker(bind=engine)
session = scoped_session(Session)
# 1 增加:add add_all
user = User(name='pyy', email='44@qq.com', extra='摄氏度法')
user1 = User(name='yyy', email='4@qq.com', extra='大沙发斯蒂芬')
book = Book(name='西游记')
session.add(user)
session.add_all([user, user1, book]) # 多个对象可以是models中任意表模型的对象
session.commit()
session.close()
# 2 基本查 filter filter_by
# filer:写条件 filter_by:等于的值
# filter
# 2.1 session.query(User) 中写表模型,可以写多个表模型(连表操作) select * from User;
# 2.2 filter 过滤条件,必须写表达式 == >= <= != select * from user where user.id=1
# 2.3 all:普通列表 first
user = session.query(User).filter(User.name == 'lqz').first()
user = session.query(User).filter(User.name != 'lqz').all()
print(user)
res = session.query(User).filter(User.id > 1).all()
print(res)
# filter_by 直接写等式 不能写成 User.name = 'lqz'
user = session.query(User).filter_by(name='lqz').first()
user = session.query(User).filter_by(id=2).first()
user = session.query(User).filter_by(id=2).first()
print(user)
# 3 删除(查到才能删) filter或filter_by查询的结果 不要all或first出来, .delete()即可
res = session.query(User).filter_by(id=2).delete()
session.commit() # 一定不要忘了
print(res) # 影响的行数
# 4 修改(查到才能改)
# 方式一:update修改
res = session.query(User).filter_by(id=3).update({"name" : "彭于晏"})
print(res)
session.commit()
# 方式二,使用对象修改
res = session.query(User).filter_by(id=3).first()
res = session.query(User).filter_by(name='zzz').first()
res.name='来来来'
print(res.id)
session.add(res) # add 如果有主键,就是修改,如果没有主键就是新增
session.commit()
3.1 高级查询
# 4 查询: filer:写条件 filter_by:等于的值
# 4.1 查询所有 是list对象
res = session.query(User).all() # 是个普通列表
print(type(res))
print(len(res))
# 4.1.1 只查询某几个字段
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])
# 4.1.2 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() # django 中使用 Q
res = session.query(User).filter_by(name='lqz099').all()
res = session.query(User).filter_by(name='lqz099',email='47@qq.com').all()
print(len(res))
# 4.2 取一个 all了后是list,list 没有first方法
res = session.query(User).first()
# 4.3 查询所有,使用占位符(了解) :value :name
select * from user where id <20 or name=lqz099
res = session.query(User).filter(text("id<:value or name=:name")).params(value=10, name='lqz099').all()
# 4.4 自定义查询(了解)
# 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)
# 4.5 高级查询
# 条件
# 表达式,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='lqz099'))).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 != ""
)).all()
# 通配符,以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%'))
# 分页
# 一页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 和分组的字段
res = 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 = 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()
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)
session = scoped_session(Session)
# 2.0.9 版本需要使用text包裹一下,原来版本不需要
# cursor = session.execute(text('select * from users'))
# result = cursor.fetchall()
# print(result)
cursor = session.execute(text('insert into books(name) values(:name)'), params={"name": '红楼梦'})
session.commit()
print(cursor.lastrowid)
session.close()
3.3 django中执行原生sql
# 选择的查询基表Book.objects.raw ,只是一个傀儡,正常查询出哪些字段,都能打印出来
def index(request):
# books = Book.objects.raw('select * from app01_book where id=1') # RawQuerySet 用起来跟列表一样
# books = Publish.objects.raw('select * from app01_book where id=1') # RawQuerySet 用起来跟列表一样
# print(books[0])
# print(type(books[0]))
# # for book in books:
# # print(book.name)
# # print(books[0].name)
# print(books[0].addr) #也能拿出来,但是是不合理的
res = Book.objects.raw('select * from app01_publish where id=1') # RawQuerySet 用起来跟列表一样
print(res[0])
print(type(res[0]))
print(res[0].name)
# book 没有addr,但是也打印出来了
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"))
# 跟数据库无关,不会新增字段,只用于快速链表操作
# 基于对象的跨表查询:就要加这个字段,取对象 person.hobby pserson.hobby_id
# 类名,backref用于反向查询
hobby = relationship('Hobby', backref='pers') # 如果有hobby对象,拿到所有人 hobby.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)
session = scoped_session(Session)
# 一对多新增
hobby = Hobby(caption='乒乓球')
session.add(hobby)
person = Person(name='张三')
session.add(person)
hobby=session.query(Hobby).filter(Hobby.caption=='乒乓球').first()
# person = Person(name='王五',hobby_id=hobby.id)
person = Person(name='王五',hobby_id=1)
session.add(person)
# 支持按对象的增加方式,必须加relationship 做关联
# 方式一
hobby=session.query(Hobby).filter(Hobby.caption=='乒乓球').first()
person = Person(name='赵六',hobby=hobby)
# 方式二
hobby = Hobby(caption='羽毛球') # 表中暂时没有
person = Person(name='赵六', hobby=hobby)
session.add_all([person, hobby])
session.commit()
## 基于对象的跨表查询 .
# 正向查询
person=session.query(Person).filter(Person.name=='王五').first()
# print(person.hobby_id)
print(person.hobby) # Hobby 的对象
# 反向查询
hobby=session.query(Hobby).filter(Hobby.id==1).first()
print(hobby.pers)
# 基于连表的查询(一会讲)
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')
def __str__(self):
return self.name
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)
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)
session = scoped_session(Session)
# 新增
# 1 笨办法新增
girl=Girl(name='刘亦菲')
boy=Boy(name='彭于晏')
session.add_all([girl,boy])
session.add(Boy2Girl(girl_id=1,boy_id=1))
session.commit()
# 2 使用relationship
boy = Boy(name='lqz')
boy.girls = [Girl(name='迪丽热巴'), Girl(name='景田')]
session.add(boy)
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 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)
# 多对多关系连表
# 多对多关系,基于链表的跨表查
#方式一:直接连
res = session.query(Boy, Girl,Boy2Girl).filter(Boy.id == Boy2Girl.boy_id,Girl.id == Boy2Girl.girl_id).all()
# 方式二:join连
res = session.query(Boy).join(Boy2Girl).join(Girl).filter(Person.id>=2).all()
分类:
Flask
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!