pythonpostgresqlalembicflask-migrate

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


I have the following model, I want to change the length of name, when I do the migration it does not detect the changes

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)) 

For example change to

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

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

But when I change the name, if it detects the changes

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

Solution

  • Update - June 2020

    Type comparison changed in Alembic 1.4, so field length changes should be more reliably identified. From the changelog:

    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.

    The change log also cites this issue and this documentation.


    Original Answer

    TL;DR:

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

    I've testing this on a string length change in PG backend and it does work, however as you can see below, the docs currently state that it should not. Here's the 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.

    And the API Reference for compare_type states:

    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.

    Lastly, in the section titled, Comparing Types, the following example is given for how to enable the type comparisons:

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

    You'll find the context.configure() call in the env.py script that is automatically generated by alembic nested inside the connect context:

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

    ... and just add the compare_type param in there.

    In the same section, they go on to say:

    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.

    So it's interesting to see it mentioned a couple of times in the docs that this should not work. As mentioned earlier, I have tested this on postgres and can confirm that setting compare_type=True does generate a revision for the length of the column, so perhaps the docs are lagging a little behind on this, or the maintainers aren't ready to declare it as a feature yet.

    I've also tested on MySQL and can confirm that string length changes are also picked up if compare_type=True.