假设我在同一个数据库表(单表继承)上有两种不同的类型:
class Employee(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String, nullable = False)
discriminator = db.Column('type', String)
__mapper_args__ = {'polymorphic_on': discriminator}
class Manager(Employee):
__mapper_args__ = {'polymorphic_identity': 'manager'}
division = db.Column(db.String, nullable = False)
role = db.Column(db.String, nullable = False)
class Worker(Employee):
__mapper_args__ = {'polymorphic_identity': 'worker'}
title = db.Column(db.String, nullable = False)
(是的,我正在使用Flask-SqlAlchemy而不是普通的vanilla)现在我该如何将一个声明性模型转换为另一个声明模型。也就是说,如果“工人”被提升为“经理人”怎么办?我怎么做?我是否必须编写原始SQL来执行此操作?
很抱歉,如果以前曾经问过这个问题,但是我无法从谷歌找到它。请注意,这是一个人为的例子。
答案 0 :(得分:10)
它是kludgy,它会引发警告,但你可以通过设置属性来强制修改鉴别器列:
john_smith = session.query(Employee).filter_by(name='john smith').one()
john_smith.discriminator = 'manager'
session.commit()
这将导致类似
的警告SAWarning: Flushing object <Worker at 0xdeadbeef> with incompatible polymorphic
identity 'manager'; the object may not refresh and/or load correctly
mapper._validate_polymorphic_identity(mapper, state, dict_)
你可以忽略它,只要你解决它会导致的问题。 最安全的事情是在提交后立即关闭会话(session.close()
)或从中删除所有内容(session.expunge_all()
)。
如果必须,只需从会话中删除John(session.expunge(john_smith)
),就可以单独修复John的对象问题。你必须要小心;对john_smith
的任何剩余引用都将保留该对象,但幸运的是,他将与session
分离,并且您不会被允许对它们做任何事情。
我也尝试了其他明显的选择。两者都没有奏效,但两者都说明了SQLAlchemy的Session
对象存储以及如何存储:
session.refresh(john_smith)
失败,
InvalidRequestError: Could not refresh instance '<Worker at 0xdeadbeef>'
那是因为SQLAlchemy在数据库中查询Worker
(不是Employee
)并且找不到John Smith的名字,因为数据库现在知道John因为他的type
列中有新的价值。
session.expire(john_smith)
成功但未能将John更新为新课程,任何后续访问都将导致
ObjectDeletedError: Instance '<Worker at 0xdeadbeef>' has been deleted, or
its row is otherwise not present.
SQLAlchemy仍然认为John是Worker
,并且它会尝试将其作为Worker
进行查询。那是因为他仍然坚持session.identity_map
,看起来像这样:
{(saexample2.Employee, (1,)): <saexample2.Worker at 0xdeadbeef>}
所以有John,明确列为Worker
对象。当您从会话中expunge()
John时,字典中的此条目将被清除。当你expire()
他时,他的所有映射属性都被标记为陈旧,但他仍然存在于字典中。
答案 1 :(得分:1)
我建议您重新设计对象模型。当一个对象的工作方式与另一个对象的属性一样好时,对象模型将从重新思考中受益的标志。在这种情况下,Worker.title
同样可以是“经理”。
另外,Manager.division作为自己的对象分区效果更好。尤其是因为一个部门可以想象与工人有一对多的关系。
可能是一个Division
对象,其中ForeignKey
经理指向Employee
个对象。 Employee
对象将具有title属性;在Employee.__init__()
中,您可以手动检查员工是否是任何部门的经理,然后将Employee.title
设置为“经理”来自__init__()
。