对SQLAlchemy中的add()、flush()、commit()操作理解

文章通过一个接口测试案例展示了SQLAlchemy中数据库操作方法的使用,包括添加对象到会话(add())、刷新会话(flush())和提交事务(commit())。问题在于未正确使用flush()导致无法获取新插入数据的ID,从而影响测试。修正后的代码确保了数据的完整性和事务的正确提交,强调了这些方法在管理数据库事务中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录


问题

下面是一个接口测试文件:

from applications.models.ri_zhi.gong_zuo_ri_zhi import GongZuoRiZhi
from applications.models.system.models import RenYuanDengLu, ZuZhiXinXi, RenYuanXinXi


class TestGongZuoWanChengQueRen:

    def test_get_zuo_ye_wan_cheng_qing_ren_yuans(self, client, db, setup_before_test):
        ren_yuan_deng_lu1 = RenYuanDengLu(ren_yuan_name='张三')
        ren_yuan_deng_lu2 = RenYuanDengLu(ren_yuan_name='李四')
        db.session.add(ren_yuan_deng_lu1)
        db.session.add(ren_yuan_deng_lu2)
        db.session.flush()
        ren_yuan_xin_xi1 = RenYuanXinXi(
            ren_yuan_id=ren_yuan_deng_lu1.id)
        ren_yuan_xin_xi2 = RenYuanXinXi(
            ren_yuan_id=ren_yuan_deng_lu2.id)
        # 01
        zuo_ye_wan_cheng_qing_ren_yuans1 = GongZuoRiZhi(
            gong_zuo_ren_id=1,
            gong_zuo_jue_se='审批文件',
            biao_dan_bian_hao='1223',
            gong_zuo_kai_shi_shi_jian='2020-01-01',
            gong_zuo_jie_shu_shi_jian='2020-01-11',
        )
        # 02
        zuo_ye_wan_cheng_qing_ren_yuans2 = GongZuoRiZhi(
            gong_zuo_ren_id=2,
            gong_zuo_jue_se='主办人',
            biao_dan_bian_hao='1223',
            gong_zuo_kai_shi_shi_jian='2020-01-01',
            gong_zuo_jie_shu_shi_jian='2020-01-11',
        )

        db.session.add(zuo_ye_wan_cheng_qing_ren_yuans1)
        db.session.add(zuo_ye_wan_cheng_qing_ren_yuans2)
        db.session.add(ren_yuan_xin_xi1)
        db.session.add(ren_yuan_xin_xi2)
        db.session.flush()
        db.session.commit()

        response1 = client.get(
            '/zuo_ye_gong_zuo_wan_cheng_qing_kuang_que_ren/get_zuo_ye_wan_cheng_qing_kuang_que_ren_ren_yuan/?tong_ji_kai_shi_shi_jian=2019-01-03&tong_ji_jie_shu_shi_jian=2021-01-05&type=流程')
        assert response1.status_code == 200
        results1 = response1.json['data']
        print(results1)
        # assert len(results1) == 1

        # 验证第一个元素的内容
        assert results1[0]['id'] == 1
        assert results1[0]['name'] == '张三'

        response2 = client.get(
            '/zuo_ye_gong_zuo_wan_cheng_qing_kuang_que_ren/get_zuo_ye_wan_cheng_qing_kuang_que_ren_ren_yuan/?tong_ji_kai_shi_shi_jian=2019-01-03&tong_ji_jie_shu_shi_jian=2021-01-05&type=作业')
        assert response2.status_code == 200
        results2 = response2.json['data']
        print(results2)
        # assert len(results2) == 1

        # 验证第一个元素的内容
        assert results2[0]['id'] == 2
        assert results2[0]['name'] == '李四'

着重看#01部分的测试用例中的这几行代码:

zuo_ye_wan_cheng_qing_ren_yuans1 = GongZuoRiZhi(
    gong_zuo_ren_id=1,
    gong_zuo_jue_se='审批文件',
    biao_dan_bian_hao='1223',
    gong_zuo_kai_shi_shi_jian='2020-01-01',
    gong_zuo_jie_shu_shi_jian='2020-01-11',
)

来看gong_zuo_ren_id这个属性,在我的数据库表关系中,gong_zuo_ren_id是一个外键,指向RenYuanXinXi表的id,由于我在这块代码之前已经在RenYuanXinXi插入了数据,数据表中的id是自动递增的。理论上说,上面的代码中gong_zuo_ren_id=1可以改成gong_zuo_ren_id=ren_yuan_xin_xi1.id,但是测试不通过,数据库中,没有返回符合条件的数据。

经过排查发现,问题出在add()flush()这两个函数上,我在创建完ren_yuan_xin_xi1之后,并没有进行add()flush()操作,这就导致当前会话的数据库中,并没有这条数据,紧接着我在ren_yuan_xin_xi1.id的时候,自然不会有id的值让我拿到。

代码应该修改成下面这样:

        ren_yuan_deng_lu1 = RenYuanDengLu(ren_yuan_name='张三')
        ren_yuan_deng_lu2 = RenYuanDengLu(ren_yuan_name='李四')
        db.session.add(ren_yuan_deng_lu1)
        db.session.add(ren_yuan_deng_lu2)
        db.session.flush()
        ren_yuan_xin_xi1 = RenYuanXinXi(
            ren_yuan_id=ren_yuan_deng_lu1.id)
        ren_yuan_xin_xi2 = RenYuanXinXi(
            ren_yuan_id=ren_yuan_deng_lu2.id)
        db.session.add(ren_yuan_xin_xi1)
        db.session.flush()
        # 01
        zuo_ye_wan_cheng_qing_ren_yuans1 = GongZuoRiZhi(
            gong_zuo_ren_id=ren_yuan_xin_xi1.id,
            gong_zuo_jue_se='审批文件',
            biao_dan_bian_hao='1223',
            gong_zuo_kai_shi_shi_jian='2020-01-01',
            gong_zuo_jie_shu_shi_jian='2020-01-11',
        )

#02测试用例同理。

涉及技术点

关于add()fulsh()commit()操作的作用:

db.session.add()db.session.flush()db.session.commit() 是 SQLAlchemy 中常用的数据库操作方法,它们通常结合使用来管理会话(Session)中的数据库事务。

  1. db.session.add()
    • 用于将新创建或修改后的实体对象添加到当前会话中。
    • 只是将对象添加到会话的临时缓存中,并不会立即执行数据库操作。
    • 在使用 add() 方法后,可以继续对对象进行修改,直到调用 flush()commit() 执行实际数据库操作。
  2. db.session.flush()
    • 将当前会话中所有未提交的数据库操作(插入、更新、删除等)发送到数据库服务器执行。
    • flush()方法通常在以下情况下使用:
      • 当需要在后续代码中获取插入记录的自增 ID(例如,MySQL 中的 AUTO_INCREMENT)时,可以在 flush() 后立即获得 ID。
      • 当需要立即检查数据库操作是否成功或是否有错误时,可以在 flush() 后查看数据库执行结果。
  3. db.session.commit()
    • 将当前会话中的所有数据库操作提交到数据库服务器。
    • commit() 方法将会话中的所有操作一起作为一个事务提交到数据库。
    • 提交后,所有之前的 flush() 操作将永久生效,且数据库事务完成。

通常,数据库操作的基本流程是:

  1. 使用 add() 将实体对象添加到会话中,可以在此阶段进行实体对象的初始化和修改。
  2. 使用 flush() 提交之前的数据库操作,以便获取自增 ID 或检查执行结果。
  3. 使用 commit() 最终将所有操作提交到数据库,实现持久化存储。

需要注意的是,在使用 commit() 提交事务后,会话将被清理,所有之前添加的对象和更改都会失去跟踪。所以,在一个会话中,通常只需要调用一次 commit()。如果希望在多个事务中执行数据库操作,可以创建多个会话,每个会话独立管理自己的事务。

### 使用SQLAlchemy进行数据插入操作 为了通过SQLAlchemy执行数据插入操作,可以采用两种主要方法:一种是利用ORM(对象关系映射),另一种则是直接使用`execute()`函数来运行原始SQL语句。这里重点介绍基于ORM的方法。 #### 创建模型类 在开始之前,需定义一个继承自`Base`的Python类作为表结构模板[^1]: ```python from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) fullname = Column(String(50)) nickname = Column(String(50)) def __repr__(self): return "<User(name='%s', fullname='%s', nickname='%s')>" % ( self.name, self.fullname, self.nickname) ``` 此段代码定义了一个名为`User`的数据模型,对应着数据库中的`users`表格,并指定了几个字段及其属性类型。 #### 初始化会话并建立连接 接着,在应用程序启动时初始化Session工厂以及创建实际使用的session实例[^2]: ```python from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine engine = create_engine('postgresql://username:password@localhost/mydatabase') Session = sessionmaker(bind=engine) # Create a new Session object. session = Session() ``` 上述代码片段展示了如何设置PostgreSQL数据库连接字符串,并绑定了该引擎到新的Session制造器上;随后每次调用`Session()`都会返回一个新的独立事务性的会话对象。 #### 插入单条记录 当准备向数据库中添加新纪录时,可以通过如下方式实现[^3]: ```python ed_user = User(name='ed', fullname='Ed Jones', nickname='edsnickname') # Add the record to the current transactional state of our session. session.add(ed_user) # Commit all changes and flush them into the database. session.commit() ``` 这段脚本首先构建了一位用户的实例化对象,之后将其加入当前活跃的会话之中等待提交至持久层存储起来。最后一步至关重要——它不仅保存了所有的更改还触发了与之关联的操作同步到了目标DBMS里边去。 #### 批量插入多条记录 如果要一次性插入多个实体,则可采取批量处理模式以提高效率[^4]: ```python new_users = [ User(name='wendy', fullname='Wendy Williams', nickname='windy'), User(name='mary', fullname='Mary Contrary', nickname='mary'), User(name='fred', fullname='Fred Flintstone', nickname='freddy') ] # Extend the existing set with multiple items at once. session.bulk_save_objects(new_users) # Remember to commit after bulk operations too! session.commit() ``` 在此处,列表形式传递给`bulk_save_objects()`方法允许一次写入若干个用户信息进入指定的目标集合内。同样地,完成这些动作后记得再次确认提交以便最终生效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值