SQLAlchemy:当 autoflush=False 时,持久对象的更新会自动刷新
SQLAlchemy: Updates on persistent object are auto flushed when autoflush=False
当对持久对象应用更新时,似乎每次更新都会自动推送到事务缓冲区,即使 autoflush
属性 设置为 false。
考虑以下示例。有两个实体 - Employee 和 Department,它们之间存在多对多关系。
ORM定义如下:
from sqlalchemy.orm import registry, relationship
mapper_registry = registry()
mapper_registry.map_imperatively(
models.Employee,
employee_table,
properties={
"employee_id": employee_table.c.EmployeeId,
"first_name": employee_table.c.FirstName,
"last_name": employee_table.c.LastName,
"departments": relationship(
models.Department, secondary=joinEmployeeDepartment
),
},
)
初始数据库状态如下所示:
员工:
EmployeeId
FirstName
FirstName
1
John
Doe
部门:
DepartmentId
DepartmentName
1
Sales
加入员工部门:
RelationshipId
EmployeeId
DepartmentId
1
1
1
然后执行以下代码:
from sqlalchemy.orm.session import Session, sessionmaker
Session = sessionmaker(bind=engine, autocommit=False, autoflush=False, expire_on_commit=False)
session = Session()
session.begin()
try:
employee: Employee = session.query(Employee).where(Employee.first_name == 'John').one_or_none()
employee.departments = [Department(department_name='support')] # 1
employee.departments = [Department(department_name='IT')] # 2
session.commit()
finally:
session.close()
由于 autoflush
已关闭,我希望当持久性员工对象在 #1 中在内存中更新时,更改不会刷新到事务缓冲区。由于#2 覆盖了#1 中所做的更改,因此在调用 session.commit()
时只有后者会被刷新和提交。然而,我观察到情况并非如此。 #1 中的更改也添加到事务缓冲区中。得到的DB状态如下:
部门:
DepartmentId
DepartmentName
1
Sales
2
support
3
IT
加入员工部门:
RelationshipId
EmployeeId
DepartmentId
2
1
3
我的问题是,当对持久对象进行更新时,是否忽略了 autoflush
设置?
发生的事情是,随着每个新的 Department
实例被创建,它被添加到 session.new
并进入 pending state。待定对象
[isn't] actually flushed to the database yet, but it will be when the next flush occurs.
因此,当会话刷新提交时,它会找到两个挂起的 Department
实例并将它们发送到数据库。
第一个 Department
实例的“删除”未在 session.deleted
中跟踪,因为该实例未处于持久状态。因此在刷新时没有为该实例发送 DELETE
。然而,SQLAlchemy 的属性跟踪将其跟踪为已从关系中删除,因此 SQLAlchemy 修复了多对多映射以反映最终所需状态。
我们可以通过打印 session.new
和关系的历史记录,并在引擎上启用日志记录来看到这一点:
with Session() as s:
employee: Employee = s.query(Employee).where(Employee.first_name == 'John').one_or_none()
hist = sa.inspect(employee).attrs.departments.history
employee.departments = [Department(department_name='support')]
print(s.new)
print(sa.inspect(employee).attrs.departments.history)
employee.departments = [Department(department_name='IT')]
print(s.new)
print(sa.inspect(employee).attrs.departments.history)
s.commit()
输出:
2021-12-08 11:42:40,134 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2021-12-08 11:42:40,139 INFO sqlalchemy.engine.Engine SELECT employees.id AS employees_id, employees.first_name AS employees_first_name, employees.last_name AS employees_last_name
FROM employees
WHERE employees.first_name = ?
2021-12-08 11:42:40,139 INFO sqlalchemy.engine.Engine [generated in 0.00028s] ('John',)
2021-12-08 11:42:40,143 INFO sqlalchemy.engine.Engine SELECT departments.id AS departments_id, departments.department_name AS departments_department_name
FROM departments, association
WHERE ? = association.employee_id AND departments.id = association.department_id
2021-12-08 11:42:40,143 INFO sqlalchemy.engine.Engine [generated in 0.00024s] (1,)
IdentitySet([<__main__.Department object at 0x7fdbf4998eb0>])
History(added=[<__main__.Department object at 0x7fdbf4998eb0>], unchanged=[], deleted=[<__main__.Department object at 0x7fdbf4882860>])
IdentitySet([<__main__.Department object at 0x7fdbf4998eb0>, <__main__.Department object at 0x7fdbf4881420>])
History(added=[<__main__.Department object at 0x7fdbf4881420>], unchanged=[], deleted=[<__main__.Department object at 0x7fdbf4882860>])
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine INSERT INTO departments (department_name) VALUES (?)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine [cached since 0.01444s ago] ('support',)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine INSERT INTO departments (department_name) VALUES (?)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine [cached since 0.01477s ago] ('IT',)
2021-12-08 11:42:40,147 INFO sqlalchemy.engine.Engine DELETE FROM association WHERE association.employee_id = ? AND association.department_id = ?
2021-12-08 11:42:40,147 INFO sqlalchemy.engine.Engine [generated in 0.00023s] (1, 1)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine INSERT INTO association (employee_id, department_id) VALUES (?, ?)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine [cached since 0.01509s ago] (1, 3)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine COMMIT
要防止第一个 Department
的插入,必须通过 expunging 从会话中将其从 session.new
中删除:
employee.departments = [Department(department_name='support')]
s.expunge(employee.departments[0])
employee.departments = [Department(department_name='IT')]
请参阅会话文档,object states 了解更多详细信息。
当对持久对象应用更新时,似乎每次更新都会自动推送到事务缓冲区,即使 autoflush
属性 设置为 false。
考虑以下示例。有两个实体 - Employee 和 Department,它们之间存在多对多关系。 ORM定义如下:
from sqlalchemy.orm import registry, relationship
mapper_registry = registry()
mapper_registry.map_imperatively(
models.Employee,
employee_table,
properties={
"employee_id": employee_table.c.EmployeeId,
"first_name": employee_table.c.FirstName,
"last_name": employee_table.c.LastName,
"departments": relationship(
models.Department, secondary=joinEmployeeDepartment
),
},
)
初始数据库状态如下所示:
员工:
EmployeeId | FirstName | FirstName |
---|---|---|
1 | John | Doe |
部门:
DepartmentId | DepartmentName |
---|---|
1 | Sales |
加入员工部门:
RelationshipId | EmployeeId | DepartmentId |
---|---|---|
1 | 1 | 1 |
然后执行以下代码:
from sqlalchemy.orm.session import Session, sessionmaker
Session = sessionmaker(bind=engine, autocommit=False, autoflush=False, expire_on_commit=False)
session = Session()
session.begin()
try:
employee: Employee = session.query(Employee).where(Employee.first_name == 'John').one_or_none()
employee.departments = [Department(department_name='support')] # 1
employee.departments = [Department(department_name='IT')] # 2
session.commit()
finally:
session.close()
由于 autoflush
已关闭,我希望当持久性员工对象在 #1 中在内存中更新时,更改不会刷新到事务缓冲区。由于#2 覆盖了#1 中所做的更改,因此在调用 session.commit()
时只有后者会被刷新和提交。然而,我观察到情况并非如此。 #1 中的更改也添加到事务缓冲区中。得到的DB状态如下:
部门:
DepartmentId | DepartmentName |
---|---|
1 | Sales |
2 | support |
3 | IT |
加入员工部门:
RelationshipId | EmployeeId | DepartmentId |
---|---|---|
2 | 1 | 3 |
我的问题是,当对持久对象进行更新时,是否忽略了 autoflush
设置?
发生的事情是,随着每个新的 Department
实例被创建,它被添加到 session.new
并进入 pending state。待定对象
[isn't] actually flushed to the database yet, but it will be when the next flush occurs.
因此,当会话刷新提交时,它会找到两个挂起的 Department
实例并将它们发送到数据库。
第一个 Department
实例的“删除”未在 session.deleted
中跟踪,因为该实例未处于持久状态。因此在刷新时没有为该实例发送 DELETE
。然而,SQLAlchemy 的属性跟踪将其跟踪为已从关系中删除,因此 SQLAlchemy 修复了多对多映射以反映最终所需状态。
我们可以通过打印 session.new
和关系的历史记录,并在引擎上启用日志记录来看到这一点:
with Session() as s:
employee: Employee = s.query(Employee).where(Employee.first_name == 'John').one_or_none()
hist = sa.inspect(employee).attrs.departments.history
employee.departments = [Department(department_name='support')]
print(s.new)
print(sa.inspect(employee).attrs.departments.history)
employee.departments = [Department(department_name='IT')]
print(s.new)
print(sa.inspect(employee).attrs.departments.history)
s.commit()
输出:
2021-12-08 11:42:40,134 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2021-12-08 11:42:40,139 INFO sqlalchemy.engine.Engine SELECT employees.id AS employees_id, employees.first_name AS employees_first_name, employees.last_name AS employees_last_name
FROM employees
WHERE employees.first_name = ?
2021-12-08 11:42:40,139 INFO sqlalchemy.engine.Engine [generated in 0.00028s] ('John',)
2021-12-08 11:42:40,143 INFO sqlalchemy.engine.Engine SELECT departments.id AS departments_id, departments.department_name AS departments_department_name
FROM departments, association
WHERE ? = association.employee_id AND departments.id = association.department_id
2021-12-08 11:42:40,143 INFO sqlalchemy.engine.Engine [generated in 0.00024s] (1,)
IdentitySet([<__main__.Department object at 0x7fdbf4998eb0>])
History(added=[<__main__.Department object at 0x7fdbf4998eb0>], unchanged=[], deleted=[<__main__.Department object at 0x7fdbf4882860>])
IdentitySet([<__main__.Department object at 0x7fdbf4998eb0>, <__main__.Department object at 0x7fdbf4881420>])
History(added=[<__main__.Department object at 0x7fdbf4881420>], unchanged=[], deleted=[<__main__.Department object at 0x7fdbf4882860>])
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine INSERT INTO departments (department_name) VALUES (?)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine [cached since 0.01444s ago] ('support',)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine INSERT INTO departments (department_name) VALUES (?)
2021-12-08 11:42:40,145 INFO sqlalchemy.engine.Engine [cached since 0.01477s ago] ('IT',)
2021-12-08 11:42:40,147 INFO sqlalchemy.engine.Engine DELETE FROM association WHERE association.employee_id = ? AND association.department_id = ?
2021-12-08 11:42:40,147 INFO sqlalchemy.engine.Engine [generated in 0.00023s] (1, 1)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine INSERT INTO association (employee_id, department_id) VALUES (?, ?)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine [cached since 0.01509s ago] (1, 3)
2021-12-08 11:42:40,148 INFO sqlalchemy.engine.Engine COMMIT
要防止第一个 Department
的插入,必须通过 expunging 从会话中将其从 session.new
中删除:
employee.departments = [Department(department_name='support')]
s.expunge(employee.departments[0])
employee.departments = [Department(department_name='IT')]
请参阅会话文档,object states 了解更多详细信息。