sqlalchemy使用
目的
总结项目中使用到sqlalchemy的部分。(python中的对象映射技术,更方便地使用)
使用
基础
安装
pip install SQLAlchemy -i https://pypi.douban.com/simple
pip install mysql-connector-python -i https://pypi.douban.com/simple
增删改查
# 主要引用自廖雪峰老师的使用SQLAlchemy
# 导入:
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# 创建对象的基类:
Base = declarative_base()
# 定义User对象:
class User(Base):
# 表的名字:
__tablename__ = 'user'
# 表的结构:
id = Column(Integer, primary_key=True)
name = Column(String(20))
# 动态添加属性
setattr(User, ‘'age', (Column('age', Integer, nullable=True)))
# 初始化数据库连接:
engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')
# 创建DBSession类型:
DBSession = sessionmaker(bind=engine)
# 创建session对象:
session = DBSession()
# 创建新User对象: #数据对象得到创建,此时为Transient状态
new_user = User(id=5, name='Bob')
# 添加到session: #数据对象被关联到session上,此时为Pending状态
session.add(new_user)
# 提交即保存到数据库: #数据对象被推到数据库中,此时为Persistent状态
session.commit()
# 关闭session:
session.close()
# 查询
user = session.query(User).filter(User.id==5).first();
# user = session.query(User).get(5); # get参数为id
# user = session.query(User).filter_by(id=5).first(); #条件
# 删除,在查询基础上
session.delete(user)
# 改,在查询基础上
user.name='Alice'
session.commit()
session.close()
数据库操作
def init_db():
"""初始化生成上述所有表,可重复执行,内部自动判断表是否存在"""
Base.metadata.create_all(engine)
def drop_db():
# 删除表
Base.metadata.drop_all(engine)
一些区分项
- filter与filter_by: 前者条件更为丰富,插入的是条件Model.item == Number; filter_by插入的是kwargs键值对,item=Number
- 查询的启动项,即session.query(xxxx).xxx()的最后一项:主要有返回单个值得first, scalar及多个值的all, 其中 first返回第一个结果或者空,scalar如果有多个结果时报错,其他情况同first。
- filter或者filter_by中,多个条件或语句逗号隔开,默认为“and”关系, or需要引入filter_by(or_(rule1, rule2))
高级
对session进行包装
@contextmanager
def scope_session(DBSession):
session = DBSession()
try:
yield session # 返回session对象
session.commit()
except Exception as e:
session.rollback() # 异常时回滚,否则之后所有orm操作全部失效
logger.error(f"{e}", exc_info=True) # exc_info=True可输出错误栈
finally:
session.close()
DBSession.remove() # 释放session,用于多线程时,防止数据库连接超时失效
# 使用方式
with scope_session(DBSession) as session:
user = session.query(User).get(5)
session使用进程池及防止长时间失效
engine = create_engine(DB_URL, # DB_URL同上
max_overflow=5, # 超过连接池大小外最多创建的连接
pool_size=10, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=7200, # 多久之后对线程池中的线程进行一次连接的回收(重置)
echo=False, # 是否回显
isolation_level="READ_COMMITTED" # 实时监控数据库变化
)
# engine修改为上面代码,使用后DBSession.remove()归还连接到线程池。
关联查询relationship
from sqlalchemy.orm import relationship, foreign, remote
from sqlalchemy import Column, Integer, String
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True)
isbn = Column(String(11), nullable=False, comment="isbn编码")
owner_id = Column(Integer, comment="拥有者")
user= relationship(
'User', # 引用的对象
uselist=True, # 结果是单个还是列表
primaryjoin=foreign(owner_id) == remote(User.id),
backref="books")
# foreign与remote可以实现在数据库不设置外键的情况下代码中以外键使用。
user.books or book.user
批量操作
User类下添加属性方法
@property
def info_dict(self):
_needs = ['id', 'name']
return {name: getattr(self, name) for name in _needs}
users = [User(id=1, name='Yin'), User(id=2, name='Yang']
with scope_session(DBSession) as session:
# 法一
session.bulk_save_objects(users) # 添加对象列表,增加参数
session.bulk_save_objects(users, return_defaults=True)#可返回修改部分,例如主键为自增,当前项id=None, 运行后users中的对象id为数据库id
# 法二
user_dicts = [user.info_dict for user in users]
session.bulk_insert_mappings(User, user_dicts) # 插入
session.bulk_update_mappings(User, user_dicts) # 更新
其他
# 从一个数据库中查询一个对象插入到另一个数据库中, 修改对象状态。
from sqlalchemy.orm import make_transient
with scope_session(DBSession1) as session:
user = session.query(User).get(5)
make_transient(user)
with scope_session(DBSession2) as target_sess:
target_sess.add(user)
# 执行mysql语句
session.execute('call mysql_procedure();')
# 对象丢失时使用或跨session使用对象
session.merge(user)
#复杂查询
user_ids = {1, 2, 3}
results = session.query(User.id, Book).filter(User.id.in_(user_ids)).join(Book, User.id == Book.owner_id).order_by(User.id).all()
# 多线程使用
from concurrent.futures import ThreadPoolExecutor, as_completed
# 先用一个session查询相关对象,将查出的对象传入函数中,在函数中新建session,再merge; 或者把session也传入。
executor = ThreadPoolExecutor(max_workers=4)
def test(user):
print(f"hello {user}")
with scope_session(DBSession) as session:
books = session.query(Book).filter(Book.owner_id== user.id).all()
for book in books:
print(f"{user.name} own book {book.isbn}")
return True
def get_result(future):
data = future.result()
print(data)
user = session.query(User).get(5)
executor.submit(test, user).add_done_callback(get_result)
#判断数据库中NULL的字段
user = session.query(User).filter(User.name.is_(None)).first();