sqlalchemy在add时无法提交

本文介绍了在使用数据库连接池时如何设置autocommit为false,并通过手动调用session.commit()来提交事务,确保数据的一致性和完整性。

在连接池当中选择autcommeit=false

在add的提交下增加

session.commit()

手动提交到数据库

<think> 我们正在讨论使用SQLAlchemy ORM的异步方式(asyncio)进行数据库操作,特别是执行add操作后如何获取新插入记录的id。 根据提供的引用,我们知道在异步环境下,我们使用`create_async_engine`创建引擎,并使用`AsyncSession`来管理会话。 在SQLAlchemy中,当我们向数据库添加一个新对象(使用`session.add()`)并提交事务(`session.commit()`)后,通常会自动填充对象的id(如果id是由数据库自动生成的,比如自增主键)。 在异步环境下,我们同样遵循这个模式,但需要使用异步的写法(即使用`await`)。 具体步骤: 1. 创建异步会话(AsyncSession)。 2. 创建要添加的模型实例。 3. 使用`session.add(instance)`将实例添加到会话。 4. 提交事务(`await session.commit()`)。提交后,如果模型的主键字段是自增的,那么数据库会生成id并返回给模型实例。 5. 此,我们可以通过实例的主键属性(比如`id`)来获取id。 6. 注意:在提交之后,我们可能还需要使用`await session.refresh(instance)`来确保获取到最新的数据库生成的值(例如数据库中的默认值或触发器设置的值),但通常对于自增主键,在提交后就可以直接访问。 但是,有我们可能希望在提交之前就获取id,但这是不可能的,因为id是由数据库在插入生成的,必须在插入操作(即提交)之后才能获得。 另外,引用中提到了异步引擎和异步会话的创建方法,我们可以参考这些设置来构建我们的异步环境。 下面是一个示例代码,展示如何添加一个用户对象并获取其id: 假设我们有一个User模型,定义如下(使用declarative_base,如引用[3]中所示): ```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, autoincrement=True) name = Column(String(50)) ``` 然后,在异步会话中执行添加操作: ```python from sqlalchemy.ext.asyncio import AsyncSession async def create_user(session: AsyncSession, username: str): # 创建User实例 new_user = User(name=username) # 添加到session session.add(new_user) # 提交事务 await session.commit() # 提交后,new_user.id就已经被数据库赋值了 user_id = new_user.id return user_id ``` 注意:这里我们使用的是`await session.commit()`,因为异步会话的commit是一个异步操作。 另外,如果我们希望在同一个事务中执行多个操作,并且需要立即获取id以便进行后续操作(比如插入有外键关联的表),那么可以在提交后直接使用,因为同一个会话中的对象状态是保持的。 但是,有我们可能注意到,在提交后,如果会话设置了`expire_on_commit=True`(默认是True),那么提交后所有对象的属性都会过期,下一次访问会从数据库重新加载。但在我们的代码中,引用[3]中设置了`expire_on_commit=False`,所以提交后对象属性不会过期,我们可以直接访问id而不触发额外的查询。 如果设置是`expire_on_commit=True`(默认情况),那么在提交后访问id属性,会触发一次查询来重新加载对象(但我们通常不需要这个,因为自增主键在提交后已经存在于对象中,即使过期了,主键属性通常也不会被清除,因为它是持久化的)。不过,为了保险起见,我们可以在提交后立即访问id,这样在对象过期前我们已经获取了id。 因此,按照上述代码,我们可以安全地获取到id。 另外,如果我们在添加多个对象后提交,那么每个对象的id也会在提交后被赋值。 总结:在异步SQLAlchemy ORM中,执行add操作并commit后,直接访问模型实例的主键属性即可获得id。 根据引用[3],我们创建异步会话工厂的方式是: ```python AsyncSessionFactory = sessionmaker( bind=engine, class_=AsyncSession, autoflush=True, expire_on_commit=False # 这里设置为False,提交后不会过期,所以可以直接访问属性 ) ``` 因此,在我们的例子中,由于`expire_on_commit=False`,我们在commit后可以直接访问id。 下面给出一个完整的示例,包括如何创建异步会话并执行添加操作: ```python from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.orm import sessionmaker # 创建引擎(使用引用[3]中的配置) engine = create_async_engine(DB_URI, ...) # 参数省略,参考引用[3] AsyncSessionFactory = sessionmaker( bind=engine, class_=AsyncSession, expire_on_commit=False ) async def main(): # 创建异步会话 async with AsyncSessionFactory() as session: # 创建一个新用户 new_user = User(name="Alice") session.add(new_user) await session.commit() # 获取id print(f"New user id: {new_user.id}") # 运行 import asyncio asyncio.run(main()) ``` 这样,我们就可以在添加后获取到新用户的id。 注意:如果数据库表的主键不是自增的(比如使用UUID或者序列),那么我们需要在创建对象就设置id,或者通过其他方式生成,这样就不需要等待提交后才获取id。 相关问题: 1. 在SQLAlchemy异步操作中,如何设置会话的`expire_on_commit`参数?它有什么作用? 2. 除了自增主键,SQLAlchemy ORM还支持哪些主键生成策略?在异步环境下如何使用? 3. 在异步会话中,如何批量添加对象并获取它们的主键id? 4. 如果在提交后需要立即使用id进行另一个异步操作,有什么最佳实践? </think> 在使用 SQLAlchemy ORM 和 asyncio 进行 `add` 操作后获取对象 ID 的方法,关键在于正确使用异步会话管理流程。以下是详细步骤和最佳实践: ### 方法步骤 1. **创建异步会话** 使用 `AsyncSession` 并设置 `expire_on_commit=False`(确保提交后对象属性可访问): ```python from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker engine = create_async_engine(DATABASE_URL, pool_recycle=3600) AsyncSessionLocal = sessionmaker( bind=engine, class_=AsyncSession, expire_on_commit=False # 关键:提交后对象属性不失效 ) ``` 2. **添加对象并提交事务** ```python async with AsyncSessionLocal() as session: new_obj = User(name="Alice") # 示例对象 session.add(new_obj) await session.commit() # 提交事务 # 此 new_obj.id 已自动填充 print(f"新增对象ID: {new_obj.id}") ``` ### 关键说明 - **ID 获取原理** 数据库(如 MySQL AUTO_INCREMENT)在提交事务后生成 ID,SQLAlchemy 通过单位工作模式(Unit of Work)自动刷新对象属性,无需额外查询。 - **`expire_on_commit=False` 的重要性** 若未设置此参数(默认 `True`),提交后对象状态会失效,需手动刷新: ```python await session.refresh(new_obj) # 避免属性过期需要此操作 ``` - **避免常见错误** - ✘ 错误:在 `commit()` 前访问 `id`(值为 `None`) - ✓ 正确:在 `await session.commit()` 后访问 `id` ### 完整流程示例 ```python # 1. 定义模型(以用户为例) class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50)) # 2. 异步添加并获取ID async def create_user(name: str): async with AsyncSessionLocal() as session: user = User(name=name) session.add(user) await session.commit() # 提交后ID自动填充 return user.id # 直接返回数据库生成的ID # 3. 调用示例 user_id = await create_user("Bob") print(f"创建的用户ID: {user_id}") ``` ### 注意事项 1. **数据库方言支持** 需确保数据库驱动支持异步(如 `asyncmy` for MySQL),连接字符串示例: `"mysql+asyncmy://user:pass@host/db"` 2. **批量操作优化** 批量添加,可使用 `returning` 语句(PostgreSQL/SQLite)或 `get_current_autoincrement_id`(MySQL),但 ORM 通常无需手动处理。 3. **事务边界** 所有操作需在 `async with session` 上下文内完成,确保会话正确关闭。 --- ### 相关问题 1. 如何在异步 SQLAlchemy 中处理批量插入后的对象 ID 集合? 2. `session.refresh()` 在异步环境中有哪些性能影响?何必须使用? 3. 不同数据库(MySQL/PostgreSQL/SQLite)的自增 ID 机制在 ORM 中有何差异? 4. 如何通过 `mapper` 事件监听异步会话的对象 ID 生成过程? [^1][^2][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值