如何从父实例创建子SQL-Alchemy实例?

时间:2020-01-31 10:09:32

标签: python class flask flask-sqlalchemy

我有两个模型,SignalTrade。我的Trade对象继承自Signal的属性,因为几乎所有属性都是相同的,但是有些关键功能和属性使其与众不同。我在Signal上写了一个名为as_trade()的方法,我想根据Trade实例上的所有数据/属性创建一个Signal实例,以便将其保存到数据库。我正在使用Deepcopy-一直在起作用,但实际上我认为这不是正确/最佳方法。是否有尝试/测试过的方式来执行这样的动作?

class Signal(db.Model):
    __tablename__ = 'signal'
    id = db.Column(db.Integer, primary_key=True)
    class_type = db.Column(db.String())

    __mapper_args__ = {
        'polymorphic_identity': 'signal',
        'polymorphic_on': class_type,
    }

    # MAIN FIELDS
    type = db.Column(db.String(20))

    def as_trade(self):
       signal_dict = self.__dict__
       for key in ['id', 'user_id', '_sa_instance_state', 'class_type']:
           signal_dict.pop(key)
       trade = Trade(**signal_dict)
       return trade

    ...

class Trade(Signal):
    __tablename__ = 'trade'
    id = db.Column(db.Integer, db.ForeignKey('signal.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity': 'trade',
    }
    ....

我应该补充,我要到的问题是,调用as_trade()时会引发关键错误。如果我在PDB中调用self.id,则会得到一个值(1)。但是,如果我调用self.__dict__,则只会得到一个InstanceState,没有任何属性。因此,它抛出一个keyError。但是有时,通过在PDB中调用self_dict和self。 dict ,我可以使字典实际出现并具有值-这是我不了解的部分。撇开它,程序在这里失败,因为给定self。它抛出keyError。 dict 仅给出InstanceState对象。但是在PDB中四处寻找并调用self。 dict 在此过程中前进了一步,就可以完成for循环,因为字典会显示所有必需的属性...?

{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x000002E694983B88>
}

更新: 其行事时髦的原因是,通过从字典中弹出_sa_instance_state,该状态正在起作用。如果我将_sa_instance_state留在字典对象中并将其传递给Trade(),则会收到错误TypeError: '_sa_instance_state' is an invalid keyword argument for Trade

1 个答案:

答案 0 :(得分:0)

如果Signal类和Trade类共享相同的数据库列,并且它们唯一的区别在于类上定义的方法,则使用单个表继承是有意义的模型。

由于Flask-SQLAlchemy神奇地定义了表名(如果未定义表名),则需要将子类的__tablename__属性显式设置为None,以表明您打算使用表名。单表构造。

然后,如果目标是将Signal对象的列的值复制到新的Trade对象,则可以按顺序遍历Signal.__table__的列从Signal获取列的值并将它们写入新的Trade实例。

循环看起来像这样:

signal = db.session.query(Signal).one()
new_trade = Trade()
for col in Signal.__table__.c:
    if col.key not in ("id", "type"):
        setattr(new_trade, col.key, getattr(signal, col.key))

这是一个完整的示例:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)


class Signal(db.Model):
    __tablename__ = "signal"
    id = db.Column(db.Integer, primary_key=True)

    # MAIN FIELDS
    type = db.Column(db.String(20))
    col1 = db.Column(db.Integer)
    col2 = db.Column(db.Integer)

    __mapper_args__ = {"polymorphic_on": "type", "polymorphic_identity": "signal"}


class Trade(Signal):

    __tablename__ = None

    __mapper_args__ = {"polymorphic_identity": "trade"}


if __name__ == "__main__":
    db.drop_all()
    db.create_all()
    db.session.add(Signal(col1=1, col2=2))
    db.session.commit()
    signal = db.session.query(Signal).one()
    new_trade = Trade()
    for col in Signal.__table__.c:
        if col.key not in ("id", "type"):
            setattr(new_trade, col.key, getattr(signal, col.key))
    db.session.add(new_trade)
    db.session.commit()
    db.session.expire_all()
    trade = db.session.query(Trade).first()
    print(trade.col1, trade.col2)