【问题标题】:Can you copy/move data between columns in a DB migration file?您可以在数据库迁移文件中的列之间复制/移动数据吗?
【发布时间】:2017-03-30 01:47:09
【问题描述】:

我有一个 SqlAlchemy/Flask 应用程序。在其中,我有一个名为 MyModelA 的现有模型。这是它的样子:

class MyModelA(db.Model):
    a_id   = db.Column(db.Integer, nullable=False, primary_key=True)
    my_field1 = db.Column(db.String(1024), nullable=True)

现在,我正在添加一个子模型MyModelB。这是它的样子:

class MyModelB(db.Model):
    b_id   = db.Column(db.Integer, nullable=False, primary_key=True)
    a_id = db.Column(db.Integer, db.ForeignKey(MyModelA.a_id), nullable=False)
    my_field2 = db.Column(db.String(1024), nullable=True)

然后我运行python manage.py migrate。这是迁移文件中显示的内容:

def upgrade():
    op.create_table('my_model_b',
    sa.Column('b_id', sa.Integer(), nullable=False),
    sa.Column('a_id', sa.Integer(), nullable=False),
    sa.Column('my_field2', sa.String(length=1024), nullable=True),
    sa.ForeignKeyConstraint(['a_id'], [u'my_model_a.a_id'], ),
    sa.PrimaryKeyConstraint('b_id')
    )

def downgrade():
    op.drop_table('my_table_b')

我想编辑此迁移,以便为MyModelA 的每个实例创建实例MyModelB 的子记录,并将MyModelB.my_field2 设置为MyModelA.my_field1。我该怎么做?

请出示升级和降级代码。

【问题讨论】:

  • 您可以在迁移脚本中编写任何数据库代码,基本限制是您不能使用 ORM 模型,而需要使用 SQLAlchemy 核心或普通 SQL。这是一篇包含一些示例的文章:julo.ch/blog/migrating-content-with-alembic。在您的示例中,降级不需要更改,顺便说一下,只需升级。
  • 米格尔,谢谢。您如何使用connection.execute() 函数进行记录插入?它没有在那里显示。以及如何获取刚刚插入的记录的主键?

标签: python sqlalchemy flask-sqlalchemy database-migration flask-migrate


【解决方案1】:

编辑:

你可以为一次性迁移做这样的事情:

db.engine.execute("INSERT INTO model_b (a_id) select a_id from model_a");

如果你真的想要 sqlalschemy 代码:

for model in db.query(ModelA).all()
    db.session.add(ModelB(a_id=model.id))
db.session.commit()

上一个答案:

您所描述的不是您通常在迁移中所做的事情。迁移更改/创建数据库的结构。如果每次创建新的 MyModelA 时都需要它发生,这听起来更像是事件:http://docs.sqlalchemy.org/en/latest/orm/events.html#session-events

class MyModelA(db.Model):
    ...


@sqlalchemy.event.listens_for(SignallingSession, 'before_flush')
def insert_model_b(session, transaction, instances):
    for instance in session.new:
        if isinstance(instance, MyModelA):
            model_b = MyModelB(a=instance)
            session.add(model_b)

此外,您的架构需要显示该关系(不仅仅是外键),以便您可以将尚未插入的 model_a 分配给 model_b.a:

class MyModelB(db.Model):
    b_id   = db.Column(db.Integer, nullable=False, primary_key=True)
    a_id = db.Column(db.Integer, db.ForeignKey(MyModelA.a_id), nullable=False)
    a = relationship("MyModelA")
    my_field2 = db.Column(db.String(1024), nullable=True)

完整代码示例:

import sqlalchemy
from sqlalchemy.orm import relationship
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.sqlalchemy import SignallingSession

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True

db = SQLAlchemy(app)

class MyModelA(db.Model):
    __tablename__ = 'model_a'
    a_id = db.Column(db.Integer, nullable=False, primary_key=True)
    my_field1 = db.Column(db.String(1024), nullable=True)

class MyModelB(db.Model):
    __tablename__ = 'model_b'

    b_id = db.Column(db.Integer, nullable=False, primary_key=True)
    a_id = db.Column(db.Integer, db.ForeignKey(MyModelA.a_id), nullable=False)
    a = relationship(MyModelA)
    my_field2 = db.Column(db.String(1024), nullable=True)


@sqlalchemy.event.listens_for(SignallingSession, 'before_flush')
def insert_model_b(session, transaction, instances):
    for instance in session.new:
        if isinstance(instance, MyModelA):
            model_b = MyModelB(a=instance)
            session.add(model_b)

db.create_all()
model_a = MyModelA()
db.session.add(model_a)
db.session.commit()

【讨论】:

  • 实际上我确实想在迁移中这样做,我不需要每次创建新的MyModelA 时都这样做。我的数据库中已经有数据,我需要以这种方式复制它。展望未来,我的应用程序将确保创建一个新的MyModelB,这将是MyModelA 的子记录并复制数据。我按照 Miguel 提供的 SQLAlchemy 文档中的说明来实现它。谢谢。
  • 知道了。如果它是一次性迁移,那么是的,查询数据库以获取 ModelA 记录并创建 ModelB 记录是您所需要的。将我的答案留给可能有不同需求的谷歌用户。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
相关资源
最近更新 更多