SqlAlchemy:将继承的类型从一转换为另一个

2024-05-19 21:14:04 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我在同一个数据库表上有两个不同的类型(单表继承):

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,而不是普通的香草)现在我该如何将一个声明性模型转换为另一个。也就是说,如果一个“工人”被提升为“经理”呢我该怎么做?我是否必须编写原始SQL来实现这一点?在

抱歉,如果这之前有人问过,但我找不到谷歌。请注意,这是一个人为的例子。在


Tags: 数据库false类型dbstringemployeeargscolumn
2条回答

它是kludgy,它会导致一个警告,但是您可以通过设置属性强行修改discriminator列:

john_smith = session.query(Employee).filter_by(name='john smith').one()
john_smith.discriminator = 'manager'
session.commit()

这会引起一个警告,比如

^{pr2}$

你可以忽略这一点,只要你能解决它会引起的问题。最安全的方法是在提交后立即关闭会话(session.close())或从中删除所有内容(session.expunge_all())。在

如果必须,您可以通过从会话中删除John(session.expunge(john_smith))来解决John对象的问题。您必须小心;对john_smith的任何剩余引用都将保留该对象,不过谢天谢地,他将与session分离,并且您不允许对它们做任何操作。在


我也尝试了其他明显的选择。两者都不起作用,但都说明了SQLAlchemy的Session对象存储的内容以及如何:

  1. session.refresh(john_smith)失败,返回

    InvalidRequestError: Could not refresh instance '<Worker at 0xdeadbeef>'
    

    这是因为SQLAlchemy在数据库中查询一个Worker(不是Employee),并且找不到一个名为John Smith的名字,因为数据库现在知道John是因为他的type列中的新值而得到提升的。

  2. 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()他时,他所有映射的属性都被标记为过时,但他仍然存在于字典中。

我建议修改你的对象模型。当一个对象和另一个对象的属性一样工作时,对象模型将从重新思考中受益。在这种情况下,Worker.title也可以是“经理”。在

还有,部门经理作为它自己的对象划分,效果更好。尤其是因为一个部门可能会和工人有一对多的关系。在

类似于一个Division对象,其中一个管理器的ForeignKey指向一个Employee对象。Employee对象将具有title属性;在Employee.__init__()中,您可以手动检查该员工是否是任何部门的经理,然后将Employee.title__init__()设置为“manager”。在

相关问题 更多 >