在创建表之后向SQLAlchemy模型添加索引

时间:2013-01-19 21:57:48

标签: python indexing sqlalchemy

我有一个flask-sqlalchemy模型:

class MyModel(db.Model):
__tablename__ = 'targets'
id = db.Column(db.Integer, primary_key=True)
url = db.Column(db.String(2048))

该表已经创建并正在使用中。我想在url属性上创建一个索引,所以我将index = True传递给它:

url = db.Column(db.String(2048), index=True)

如何在不删除和重新创建表的情况下使此索引生效?

7 个答案:

答案 0 :(得分:27)

给出原始问题的模型类。

class MyModel(db.Model):
    __tablename__ = 'targets'
    id = db.Column(db.Integer, primary_key=True)
    url = db.Column(db.String(2048))

您不能只添加index=True,因为即使您调用db.Model.metadata.create_all(),也不会在已创建的表上创建索引。

相反,您需要创建一个独立的Index对象,然后创建它。它看起来像这样:

class MyModel(db.Model):
    __tablename__ = 'targets'
    id = db.Column(db.Integer, primary_key=True)
    url = db.Column(db.String(2048))

mymodel_url_index = Index('mymodel_url_idx', MyModel.url)

if __name__ == '__main__':
    mymodel_url_index.create(bind=engine)

现在engine来自哪里将取决于你的sqlalchemy配置,但是这段代码应该传达需要发生的事情的要点。

答案 1 :(得分:6)

答案 2 :(得分:6)

如果您正在使用Alembic,则可以创建迁移来执行此操作,并避免将其添加到模型中:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_index('table1_id', 'table1', ['id'], unique=True)
    op.create_index('table2_id', 'table2', ['id'], unique=True)
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('table1_id', table_name='table1')
    op.drop_index('table2_id', table_name='table2')
    # ### end Alembic commands ###

答案 3 :(得分:2)

自从提出问题以来,已经为此添加了支持。

现在,您只需将index=True添加到现有列中,并自动生成迁移。

已检查以下软件包版本:

alembic==1.0.10
SQLAlchemy==1.3.4
SQLAlchemy-Utils==0.34.0
Flask-SQLAlchemy==2.4.0
Flask-Migrate==2.5.2

答案 4 :(得分:1)

请注意,这是错误且过于复杂的回答

正确的方法是使用index.create,因为它是here


首先确保您拥有数据库的最新快照,并且能够从此快照还原数据库。

对于中型和大型项目(您可能需要同时支持多个版本并安装在多个环境中),有一个特殊的过程,它是数据库管理生命周期的一部分,称为“数据库迁移”。数据库迁移包括对现有模式的更改。 SQLAlchemy不支持开箱即用的迁移。

有两种SQLAlchemy兼容的数据库迁移工具可用:

在SQLAlchemy文档页面中查看有关这些工具的更多信息和链接:Altering Schemas through Migrations

但是如果您正在处理小项目,我建议您从数据库命令行实用程序或通过python脚本中的connection.execute()手动运行ALTER TABLE DDL查询。

在我正在使用的生产应用程序中,我们只支持一个最新版本的应用程序。对于每个数据库架构更改,我们执行以下步骤:

  • 制作生产数据库的快照
  • 在开发环境中加载此快照
  • 更新sqlalchemy数据模型模块
  • 准备并运行alter table query并保存此查询以供日后使用
  • 对代码进行其他相关更改
  • 在开发环境中运行测试
  • 将最新版本的代码部署到生产
  • 在生产中改变表格

此外,我正在使用以下技巧来生成创建表/索引查询: 我将我的应用程序指向全新的数据库,启用sqlalchemy查询的记录并运行metadata.create_all() - 所以在日志(或STDOUT)中我看到由sqlalchemy生成的创建查询

根据您使用的数据库系统索引创建查询会有所不同。 通用查询如下所示:

create index targets_i on targets(url);

答案 5 :(得分:-1)

我不确定这是否符合最佳做法,但我发现Alembic会通知我__table_args__中的索引,但实际上并不是在迁移期间为我做的。我创建了这个小脚本,可以生成__table_args__属性中的新索引。它如上所述使用Index.create(),但如果它们不存在则会生成新索引。

def create_indexes(db, drop_index=False):
    """
    Creates all indexes on models in project if they do not exists already. Assumes all models
    inherit from RequiredFields class, otherwise will need to adjust search for subclasses. If the index
    exists SQLAlchemy throws an error and we assume everything is ok. 
    :param db: The app db object, acts as the engine param for the Index.create()
    :param drop_index: specifies whether the indexes should be dropped or created
    :return:
    """
    from application.base_models import RequiredFields
    from sqlalchemy import Index
    from sqlalchemy.exc import ProgrammingError
    for klass in RequiredFields.__subclasses__():
        if hasattr(klass, '__table_args__'):
            for item in getattr(klass, '__table_args__'):
                if isinstance(item, Index):
                    try:
                        if not drop_index:
                            item.create(db.engine)
                        else:
                            item.drop(db.engine)
                    except ProgrammingError:  # If index exists, creation fails on error
                        pass
    return

这是一个显示索引的示例类。

class MyModel(RequiredFields):

    __table_args__ = (
         db.Index( ... ),
         db.Index( ... ),
    )

答案 6 :(得分:-6)

使用flask-migrate.It很酷。 添加索引后,只需使用以下命令:

python manage.py db migrate

一切正常