在Alembic迁移中使用SQLAlchemy ORM:我该怎么做?

时间:2012-12-03 03:27:12

标签: python sqlalchemy alembic

我目前有一个包含HTML标记的列。在该标记内,有一个我想要存储在新列中的时间戳(因此我可以查询它)。我的想法是在一次迁移中执行以下操作:

  1. 为数据创建一个新的可为空的列
  2. 使用ORM撤回我需要解析的HTML
  3. 每行
    1. 解析HTML以提取时间戳
    2. 更新ORM对象
  4. 但是当我尝试运行迁移时,它似乎陷入无限循环。这是我到目前为止所得到的:

    def _extract_publication_date(html):
        root = html5lib.parse(html, treebuilder='lxml', namespaceHTMLElements=False)
        publication_date_string = root.xpath("//a/@data-datetime")[0]
        return parse_date(publication_date)
    
    
    def _update_tip(tip):
        tip.publication_date = _extract_publication_date(tip.rendered_html)
        tip.save()
    
    
    def upgrade():
        op.add_column('tip', sa.Column('publication_date', sa.DateTime(timezone=True)))
        tips = Tip.query.all()
        map(tips, _update_tip)
    
    
    def downgrade():
        op.drop_column('tip', 'publication_date')
    

3 个答案:

答案 0 :(得分:10)

在使用@velochy的答案进行了一些实验之后,我决定在Alembic中使用SqlAlchemy的以下模式。这对我来说非常有用,可以作为OP问题的一般解决方案:

from sqlalchemy.orm.session import Session
from alembic import op

def upgrade():
    # Attach a sqlalchemy Session to the env connection
    session = Session(bind=op.get_bind())

    # Perform arbitrarily-complex ORM logic
    instance1 = Model1(foo='bar')
    instance2 = Model2(monkey='banana')

    # Add models to Session so they're tracked
    session.add(instance1)
    session.add(instance2)

def downgrade():
    # Attach a sqlalchemy Session to the env connection
    session = Session(bind=op.get_bind())

    # Perform ORM logic in downgrade (e.g. clear tables)
    session.query(Model2).delete()
    session.query(Model1).delete()

这种方法似乎可以正确处理交易。经常在处理这个问题时,我会生成数据库异常,他们会按预期回滚。

答案 1 :(得分:4)

对我来说有用的是通过执行以下操作来获得会话:

connection = op.get_bind()
Session = sa.orm.sessionmaker()
session = Session(bind=connection)

答案 2 :(得分:1)

继续发表评论,你可以尝试这样的事情:

import sqlalchemy as sa


tip = sa.sql.table(
    'tip',
    sa.sql.column('id', sa.Integer),
    sa.sql.column('publication_date', sa.DateTime(timezone=True)),
)


def upgrade():
    mappings = [
        (x.id, _extract_publication_date(x.rendered_html))
        for x in Tip.query
    ]

    op.add_column('tip', sa.Column('publication_date', sa.DateTime(timezone=True)))

    exp = sa.sql.case(value=tip.c.id, whens=(
        (op.inline_literal(id), op.inline_literal(publication_date))
        for id, publication_date in mappings.iteritems()
    ))

    op.execute(tip.update().values({'publication_date': exp}))


def downgrade():
    op.drop_column('tip', 'publication_date')