为什么 Flask Migrations 没有检测到字段的长度变化?

Why Flask Migrations does not detect a field's length change?

我有以下模型,我想更改名称的长度,当我进行迁移时它没有检测到更改

class Client(db.Model):
    __tablename__ = "client"
    client_id = db.Column(
        db.Integer,
        primary_key=True,
        autoincrement=True
    )
    name = db.Column(db.String(65))
    email = db.Column(db.String(255)) 

例如改为

name = db.Column(db.String(100))

NFO [alembic.env] No changes in schema detected.

但是当我更改名称时,如果它检测到更改

INFO  [alembic.autogenerate.compare] Detected added column 'client.name_test'
INFO  [alembic.autogenerate.compare] Detected removed column 'client.name'

更新 - 2020 年 6 月

Alembic 1.4 中的类型比较已更改,因此应更可靠地识别字段长度更改。来自更新日志:

A major rework of the “type comparison” logic is in place which changes the entire approach by which column datatypes are compared. Types are now compared based on the DDL string generated by the metadata type vs. the datatype reflected from the database. This means we compare types based on what would actually render and additionally if elements of the types change like string length, those changes are detected as well. False positives like those generated between SQLAlchemy Boolean and MySQL TINYINT should also be resolved. Thanks very much to Paul Becotte for lots of hard work and patience on this one.

更改日志还引用了this issue and this documentation


原答案

TL;DR:

context.configure(
    # ...
    compare_type = True
)

我已经在 PG 后端的字符串长度变化上对此进行了测试,它确实有效,但是正如您在下面看到的,文档目前声明它不应该。这是 relevant section of the docs:

Autogenerate can optionally detect:

  • Change of column type. This will occur if you set the EnvironmentContext.configure.compare_type parameter to True, or to a custom callable function. The default implementation only detects major type changes, such as between Numeric and String, and does not detect changes in arguments such as lengths, precisions, or enumeration members. The type comparison logic is extensible to work around these limitations, see Comparing Types for details.

API compare_type 的参考指出:

Indicates type comparison behavior during an autogenerate operation. Defaults to False which disables type comparison. Set to True to turn on default type comparison, which has varied accuracy depending on backend. See Comparing Types for an example as well as information on other type comparison options.

最后,在标题为 Comparing Types 的部分中,以下示例给出了如何启用类型比较:

context.configure(
    # ...
    compare_type = True
)

您会在 env.py 脚本中找到 context.configure() 调用,该脚本由嵌套在连接上下文中的 alembic 自动生成:

with connectable.connect() as connection:
    context.configure(
        connection=connection, target_metadata=target_metadata
    )

... 并在其中添加 compare_type 参数。

在同一节中,他们继续说:

Note The default type comparison logic (which is end-user extensible) currently works for major changes in type only, such as between Numeric and String. The logic will not detect changes such as:

  • changes between types that have the same “type affinity”, such as between VARCHAR and TEXT, or FLOAT and NUMERIC

  • changes between the arguments within the type, such as the lengths of strings, precision values for numerics, the elements inside of an enumeration.

Detection of these kinds of parameters is a long term project on the SQLAlchemy side.

所以有趣的是看到它在文档中多次提到这不应该起作用。如前所述,我已经在 postgres 上对此进行了测试,并且可以确认设置 compare_type=True 确实会生成列长度的修订版,因此文档可能在这方面有点落后,或者维护者还没有准备好尚未将其声明为一项功能。

我还在 MySQL 上进行了测试,可以确认如果 compare_type=True.

也可以检测到字符串长度的变化