现有实例上的SQLAlchemy类继承

时间:2015-03-21 15:28:30

标签: python inheritance sqlalchemy

我正在使用SQLAlchemy的连接表继承模式,但是使用精确模式in their docs,我无法弄清楚如何将子类应用于父类的现有实例。

在他们的例子中:

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Engineer(Employee):
    __tablename__ = 'engineer'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    engineer_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'engineer',
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
    }

...想象一下,我已经有了一个既不是工程师也不是经理的员工,但随后他们得到晋升。做这样的事......

employee = Employee.query.get(id)
m = Manager()
m.employee = employee
m = session.merge(employee)

...导致此错误......

FlushError: New instance <Manager at 0x7f6bf5a35490> with identity key (<class 'models.Employee'>, conflicts with persistent instance <Employee at 0x7f6bf590d210>

那么,我该如何宣传某人?

2 个答案:

答案 0 :(得分:4)

使用单独的表(在SQLAlchemy中映射到不同的类)来完全代表不同角色中的同一个实体介于非常可疑和彻底的反模式之间 - 正是因为你的问题&# 39;重新遇到:一个实体的角色可以更改(这里,通过促销),但更改表示同一实体的SQL表/ SQLAlchemy类完全运行到你遇到的那种问题刚见过。

我建议您更改数据模型 - 例如,向所有员工添加一个字段,以确定他们是否是经理,并完全丢失Manager表。如果您坚持要保留此数据模型,促销活动就会变得非常有用......:

  1. 复制正在内存中提升的员工身份的所有数据

  2. 从数据库中删除所述实体

  3. 仅限现在,创建管理器实体,将您保存的数据(1)复制到其中,并将其保存到数据库中

  4. 我无法想象保持架构现在正在使用中值得这些复杂化......

答案 1 :(得分:1)

免责声明:您应该保留@ AlexMartelli的答案。因为他是对的,只是因为......嗯......他是 THE Alex Martelli,有他自己的维基百科页面和所有('nuf说),但我正在努力无论如何,在我看到他之前回答,我要发布它。如果有的话,就像一个可以澄清SqlAlchemy如何处理Joined Table继承的小例子。

查询(Manager)时SqlAlchemy执行的操作是manager表,获取所有属性,还JOIN “parent”表(employee)通过id的{​​{1}}并将其余部分带入您的实例,这样您就可以通过改变关系来“欺骗” SqlAlchemy “手动”(引擎的ForeignKey参数对于查看SqlAlchemy正在做什么非常有用)

恕我直言,我在下面复制的是非常气馁,我绝不会把这样的东西带到实际制作中,但是......我真的很喜欢SqlAlchemy,我喜欢分享这个很少有人知道它对任何想听的人的能力(对于那些不想听的人,我追逐他们并对他们发表意见: - D

它出现了(一些评论内联):

echo=True

输出:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import CreateSchema
from sqlalchemy import event

Base = declarative_base()

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Engineer(Employee):
    __tablename__ = 'engineer'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    engineer_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'engineer',
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
    }

if __name__ == '__main__':
    engine = create_engine("mysql://root:password@localhost/tests?charset=utf8",
                           echo=True)
    Base.metadata.create_all(engine)
    Session = sessionmaker()
    Session.configure(bind=engine)
    session = Session()
    employee = Employee(name="Pepe")
    manager = Manager(name="Foobar", manager_name="Mr. Baz")
    session.add_all([employee, manager])
    session.commit()
    """ 
    table tests.employee looks like:
    |  id |   name   |    type    |
    |  1  |   Pepe   |  employee  |
    |  2  |   Foobar |   manager  |

    table tests.manager looks like:
    |  id |   manager_name   |
    |  2  |     Mr. Baz      |

    """
    to_promote = session.query(Employee).filter_by(name="Pepe").first()
    managers = [manager.name for manager in session.query(Manager).all()]
    session.close()

    print ("As of now (point 1), got %s managers: %s"
           % (len(managers), managers))
    print "Employee to_promote: %s" % to_promote.name

    connection = engine.connect()
    connection.execute(
        "START TRANSACTION;"
        "UPDATE employee SET employee.type='manager' WHERE employee.id={0};"
        "INSERT INTO manager (id, manager_name) VALUES ({0}, 'New Pepe');"
        "COMMIT;".format(to_promote.id)
    )
    connection.close()

    """ 
    table tests.employee looks like:
    |  id |   name   |    type    |
    |  1  |   Pepe   |   manager  |
    |  2  |   Foobar |   manager  |

    table tests.manager looks like:
    |  id |   manager_name   |
    |  1  |    New Pepe      |
    |  2  |     Mr. Baz      |

    """
    session = Session()
    managers = [manager.name for manager in session.query(Manager).all()]
    print ("As of now (point 2), got %s managers: %s"
           % (len(managers), managers))