23

作为 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

我不是很理解这个错误。我该如何解决这个问题,或者做这样的操作是不可能的?

4

3 回答 3

18

从您提供的代码摘录中很难理解您到底想要实现什么。但我会尝试猜测。所以下面的答案将基于我的猜测。

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

该错误表明 SmsDelivery 是一个映射器对象 - 所以它指向某个表。映射器对象应绑定到有效的 sqlalchemy 连接。

这告诉我您跳过了 DB 对象的初始化(连接并将此连接绑定到映射器对象),就像您通常在应用程序代码中所做的那样。

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

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

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

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

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 表达式语言教程中描述的几乎所有“可执行”的东西。
于 2012-08-16T21:19:00.773 回答
16

值得注意的是,如果您这样做,您可能希望在迁移中冻结您的 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),因为您的类型可能会在某个时候消失,并且您的迁移将再次失败。

于 2013-09-18T03:16:37.010 回答
0

您还需要导入 Base 然后

Base.metatada.bind = op.get_bind()

在此之后,您可以像往常一样使用您的模型而不会出错。

于 2013-05-19T11:23:23.873 回答