修改数据作为alembic升级的一部分

时间:2012-08-15 13:53:19

标签: python sqlalchemy alembic

我想修改一些数据库数据,作为alembic升级的一部分。

我以为我可以在迁移升级中添加任何代码,但以下操作失败:

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('smsdelivery', sa.Column('sms_message_part_id', sa.Integer(), sa.ForeignKey('smsmessagepart.id'), nullable=True))
    ### end Alembic commands ###

    from volunteer.models import DBSession, SmsDelivery, SmsMessagePart

    for sms_delivery in DBSession.query(SmsDelivery).all():
        message_part = DBSession.query(SmsMessagePart).filter(SmsMessagePart.message_id == sms_delivery.message_id).first()
        if message_part is not None:
            sms_delivery.sms_message_part = message_part

出现以下错误:

sqlalchemy.exc.UnboundExecutionError: Could not locate a bind configured on mapper Mapper|SmsDelivery|smsdelivery, SQL expression or this Session

我并不是真的理解这个错误。我怎么能解决这个问题呢?或者这样的操作不可能吗?

3 个答案:

答案 0 :(得分:16)

很难从您提供的代码摘录中了解到您想要实现的目标。但我会猜测。所以下面的答案将基于我的猜测。

第4行 - 您从模块导入内容(DBSession,SmsDelivery,SmsMessagePart),然后您尝试使用这些对象,就像在应用程序中一样。

错误显示SmsDelivery是一个映射器对象 - 因此它指向某个表。映射器对象应绑定到有效的sqlalchemy连接。

这告诉我你跳过了DB对象的初始化(连接并将这个连接绑定到mapper对象),就像你通常在应用程序代码中那样。

DBSession看起来像SQLAlchemy会话对象 - 它也应该有连接绑定。

Alembic已经准备好并且已经打开连接 - 用于对使用op。*方法请求的数据库模式进行更改。

所以应该有办法获得这种联系。

根据Alembic手册,op.get_bind()将返回当前的Connection绑定:
要与已连接的数据库进行完全交互,请使用上下文中提供的“绑定”:

from alembic import op
connection = op.get_bind()

因此,您可以使用此连接将查询运行到db。

PS。我想你想对表中的数据进行一些修改。您可以尝试将此修改公式化为一个更新查询。 Alembic有执行此类更改的特殊方法 - 因此您无需处理连接 alembic.operations.Operations.execute

execute(sql, execution_options=None)

使用当前迁移上下文执行给定的SQL。

在SQL脚本上下文中,语句直接发送到输出流。但是,没有返回结果,因为此函数面向生成可以在“离线”模式下运行的更改脚本。

参数:sql - 任何合法的SQLAlchemy表达式,包括:

  • 一个字符串sqlalchemy.sql.expression.text()构造。
  • sqlalchemy.sql.expression.insert()构造。
  • sqlalchemy.sql.expression.update(),
  • sqlalchemy.sql.expression.insert()或
  • sqlalchemy.sql.expression.delete()构造。几乎任何东西 这是SQL表达式语言教程中描述的“可执行文件”。

答案 1 :(得分:14)

值得注意的是,如果你这样做,你可能想要在迁移中冻结你的orm模型的副本,如下所示:

class MyType(Base):
  __tablename__ = 'existing_table'
  __table_args__ = {'extend_existing': True}
  id = Column(Integer, ...)
  ..

def upgrade():
  Base.metadata.bind = op.get_bind()

  for item in Session.query(MyType).all():
    ...

否则,您将不可避免地遇到您的模型更改以及先前的迁移不再起作用的情况。

特别要注意您要扩展Base,而不是基本类型本身(app.models.MyType),因为您的类型可能会消失,而您的迁移将再次失败。

答案 2 :(得分:0)

您还需要导入Base,然后

Base.metatada.bind = op.get_bind()

在此之后,您可以像往常一样使用您的模型。

相关问题