首页 > 解决方案 > 如何从父实例创建子 SQL-Alchemy 实例?

问题描述

我有两个模型,SignalTrade。我的Trade对象从 继承其属性Signal,因为几乎所有属性都是相同的,但是有一些关键功能和属性使其不同。我正在编写一个方法Signalas_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()被调用时,它会引发一个关键错误。如果我调用self.idPDB,我会得到一个值 (1)。但是,如果我打电话,self.__dict__我只会得到一个 InstanceState,没有任何属性。因此,它会抛出一个 keyError。但有时,通过调用 self_dict 和 self. dict而在 PDB 中,我可以让字典实际出现并具有值 - 这是我不明白的部分。不理会,程序在这里失败,因为它在给定 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

标签: pythonclassflaskflask-sqlalchemy

解决方案


如果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)

推荐阅读