【问题标题】:Adding indexes to SQLAlchemy models after table creation创建表后向 SQLAlchemy 模型添加索引
【发布时间】:2013-01-19 21:57:47
【问题描述】:

我有一个烧瓶-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)

如何在不删除重新创建表的情况下让这个索引生效?

【问题讨论】:

    标签: python indexing sqlalchemy


    【解决方案1】:

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

    现在您只需将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
    

    【讨论】:

    • 我很高兴看到这个。我刚刚尝试在相关库中搜索对此功能的引用,但找不到任何对它的引用。我认为如果我/您/某人可以挖掘到引用此功能的文档或更改日志的链接并将其包含在此处,那将非常有帮助。
    • 如果有人正在寻求资源支持,这里是索引 API 的文档。在第二个示例中,它表明您可以这样做Column("name", String(50), index=True)
    【解决方案2】:

    给定原始问题中的模型类。

    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 配置,但此代码应该传达需要发生的事情的要点。

    【讨论】:

    • @PascalVKooten 索引的名称:'mymodel_url_idx'(在这种情况下),可以设置为任何值,但在数据库中必须是全局唯一的。
    • 应该在什么时候执行mymodel_url_index.create(bind=engine)?如果我使用 alembic,这会改变吗?
    • @learn2day mymodel_url_index.create(bind=engine) 可以随时执行,但只能执行一次。运行该行将创建索引。我无法评论 alembic,因为我不知道它是如何工作的,但我希望迁移实用程序可以为您创建索引。
    • 可以在with_hint()中使用这种方法创建的索引吗?如果是,我应该使用哪种语法?
    【解决方案3】:

    请注意这是不正确且过于复杂的答案

    正确的方法是使用index.create,正如here所说的那样。


    首先确保您拥有数据库的最新快照并且能够从该快照恢复数据库。

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

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

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

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

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

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

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

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

    create index targets_i on targets(url);
    

    【讨论】:

    • 对不起,这个答案是错误的。 ALTER TABLE 不用于索引。 SQLAlchemy 非常直接地使用Index.create() 支持“CREATE INDEX”,以及使用CreateIndex DDL 构造来实现更复杂的脚本编写情况。没有必要获取“create_all()”输出或类似的东西。
    • 感谢您的指出。我同意 - 我描述了如何实施和支持有问题的方法。并且完全忘记了简单快捷的index.create
    【解决方案4】:

    我不确定这是否符合最佳实践,但我发现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( ... ),
        )
    

    【讨论】:

      【解决方案5】:

      使用烧瓶迁移。很酷。 添加索引后,只需使用以下命令:

      python manage.py db migrate
      

      一切正常

      【讨论】:

        【解决方案6】:

        【讨论】:

        • 这个答案不知道从哪里从原始问题的代码中获取这个索引对象。浏览文档以找到这一点很费劲,而且老实说几乎没有帮助,因为他们没有涉及使用声明性 api(在原始问题中使用)。对于像我一样困惑并且不想抓取文档和源代码的其他人,请参阅我的以下答案。
        【解决方案7】:

        你必须清楚两个阶段:

        1. 您的 ORM:您的数据模型是什么样的
        2. 您的迁移进度:将设计中的数据模型转换为数据库表

        Add index=True 仅更新您设计的 ORM。您应该检查您的迁移进度 - 他们如何根据您的 ORM 创建您的数据库表。您没有提到迁移,所以我认为您忘记了它们。

        我建议尝试积极管理您的迁移流程 - 例如了解 Alembic 及其工作原理。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-05-09
          • 1970-01-01
          • 2012-01-04
          • 2016-03-22
          • 2021-10-01
          • 2018-02-27
          • 1970-01-01
          相关资源
          最近更新 更多