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))

# 初始化数据库连接:
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)

一些区分项

  1. filter与filter_by: 前者条件更为丰富,插入的是条件Model.item == Number; filter_by插入的是kwargs键值对,item=Number
  2. 查询的启动项,即session.query(xxxx).xxx()的最后一项:主要有返回单个值得first, scalar及多个值得all, 其中 first返回第一个结果或者空,scalar如果有多个结果时报错,其他情况同first。
  3. 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)

参考

廖雪峰 https://www.liaoxuefeng.com/wiki/1016959663602400/1017803857459008

K.Takanashi https://www.cnblogs.com/franknihao/p/7268752.html

猜你喜欢

转载自www.cnblogs.com/coding4joy/p/12524703.html