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

时间:2012-02-02 09:30:58

标签: python sqlalchemy unique-constraint

假设有一个具有唯一名称的对象。现在要切换两个对象的名称:

这是布局:

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。有没有办法在一次提交中切换这些字段而没有这个错误?

3 个答案:

答案 0 :(得分: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.

答案 1 :(得分:4)

更纯粹的选择是删除a,重命名b,然后重新添加重命名的:

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

答案 2 :(得分:1)

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

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

以这种方式切换两个普通参数是绝对可以的,但是在你的情况下没有测试过,也许你可以尝试一下?