如何在一个提交中使用SQLAlchemy交换唯一行的两个字段?

7 投票
4 回答
1374 浏览
提问于 2025-04-17 12:10

假设你有一个对象,它的名字是独一无二的。现在你想要交换两个对象的名字:

这里是布局:

import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class MyObject(Base):
  __tablename__ = 'my_objects'
  id = sa.Column(sa.Integer, primary_key=True)
  name = sa.Column(sa.Text, unique=True)

if __name__ == "__main__":
  engine = sa.create_engine('sqlite:///:memory:', echo=True)
  Session = orm.sessionmaker(bind=engine)
  Base.metadata.create_all(engine)
  session = Session()

我想要这样做:

a = MyObject(name="Max")
b = MyObject(name="Moritz")
session.add_all([a, b])
session.commit()

# Now: switch names!
tmp = a.name
a.name = b.name
b.name = tmp
session.commit()

这样做会出现一个叫做 IntegrityError 的错误。有没有办法在一次提交中交换这些字段,而不出现这个错误呢?

4 个回答

0

Python 允许使用这种语法(用元组):

a.name, b.name = b.name, a.name

这样交换两个普通参数是完全可以的,不过在你的情况下没有经过测试,或许你可以试试看?

5

一个更简单的方法是先删除a,然后把b改个名字,最后再把a以新名字加回来:

session.delete(a)
sqlalchemy.orm.session.make_transient(a)
a.name, b.name = b.name, a.name
session.flush()
session.add(a)
session.commit()
4

你在名字字段里设置了 unique=True,这意味着每个名字都必须是唯一的。所以当你尝试提交的时候,它会执行更新操作,这时就会出现错误。

具体来说,当你修改名字时,这个新名字会先在内存中被记录下来。但是当系统尝试执行更新操作时,发现数据库里已经有一个相同名字的记录了,因此就会报 IntegrityError 的错误。

要更改名字的方法是

a = MyObject(name="Max")
b = MyObject(name="Moritz")
session.add_all([a, b])
session.commit()

# Now: switch names!
atmp = a.name
btemp = b.name

a.name = a.name+btemp # Temp set the any random name
session.commit()

b.name = atemp
a.name = btemp
session.commit() # Run the update query for update the record.

撰写回答