without migrations manager manage generate sqlalchemy flask database-migration flask-sqlalchemy alembic

sqlalchemy - migrations - manage py flask



No se detectaron cambios en la autogeneración alambique de migraciones con Flask-SQLAlchemy (4)

Estoy teniendo problemas para que Alembic db.Model migraciones candidatas de los cambios a las clases usando db.Model (Flask-SQLAlchemy) en lugar de Base .

He modificado env.py para crear mi aplicación Flask, importar todos los modelos relevantes, inicializar la base de datos y luego ejecutar migraciones:

... uri = ''mysql://user:password@host/dbname?charset=utf8'' app = Flask(__name__) app.config[''SQLALCHEMY_DATABASE_URI''] = uri app.config[''SQLALCHEMY_ECHO''] = True db.init_app(app) with app.test_request_context(): target_metadata = db.Model.metadata config.set_main_option(''sqlalchemy.url'', uri) if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() ...

Este enfoque funciona bien para drop_all() , create_all() (por ejemplo, cuando se recrea una base de datos de prueba para la prueba de unidad), pero en este caso parece que no funciona. Los scripts de versión generados automáticamente siempre tienen métodos de actualización y degradación vacíos, por ejemplo ,

def upgrade(): ### commands auto generated by Alembic - please adjust! ### pass ### end Alembic commands ### def downgrade(): ### commands auto generated by Alembic - please adjust! ### pass ### end Alembic commands ###

Mis cambios han incluido renombrar columnas, cambiar definiciones de columnas, etc. , no solo cambios a índices y claves externas.

¿Hay alguien por ahí usando Alembic con Flask-SQLAlchemy? ¿Alguna idea de dónde voy mal?

¡Muchas gracias!


Alembic no puede detectar automáticamente nombres de tablas o columnas. Por defecto, tampoco buscará cambios en el tipo de columna, pero la opción compare_type puede habilitarse para esto.

Extracto de la documentación de Alembic:

Autogenerate detectará por defecto:

  • Adiciones de tablas, eliminaciones.
  • Adiciones de columnas, eliminaciones.
  • Cambio de estado anulable en columnas.

Autogenerate puede opcionalmente detectar:

  • Cambio de tipo de columna. Esto ocurrirá si establece compare_type=True en EnvironmentContext.configure() . La función funciona bien en la mayoría de los casos, pero está desactivada de manera predeterminada para que pueda probarse primero en el esquema de destino. También se puede personalizar pasando un callable aquí; Consulte la documentación de la función para más detalles.
  • Cambio de servidor por defecto. Esto ocurrirá si establece compare_server_default=True en EnvironmentContext.configure() . Esta característica funciona bien para casos simples pero no siempre puede producir resultados precisos. El backend de Postgresql realmente invocará los valores "detectados" y "metadatos" en la base de datos para determinar la equivalencia. La función está desactivada de forma predeterminada para que se pueda probar primero en el esquema de destino. Al igual que la comparación de tipos, también se puede personalizar pasando un llamable; Consulte la documentación de la función para más detalles.

Autogenerate no puede detectar:

  • Cambios de nombre de tabla. Estos saldrán como una adición / caída de dos tablas diferentes, y en su lugar deberían ser editados a mano para cambiar el nombre.
  • Cambios de nombre de columna. Al igual que los cambios en el nombre de la tabla, estos se detectan como un par de columnas agregar / soltar, que no es lo mismo que un cambio de nombre.
  • Los tipos especiales de SQLAlchemy, como Enum cuando se generan en un backend que no son compatibles con ENUM directamente, esto se debe a que la representación de dicho tipo en la base de datos no compatible, es decir, una restricción CHAR+CHECK , podría ser cualquier tipo de CHAR+CHECK . Para SQLAlchemy determinar que esto es realmente un ENUM solo sería una suposición, algo que generalmente es una mala idea. Para implementar su propia función de "adivinación" aquí, use el evento sqlalchemy.events.DDLEvents.column_reflect() para alterar el tipo SQLAlchemy pasado para ciertas columnas y posiblemente sqlalchemy.events.DDLEvents.after_parent_attach() para interceptar restricciones CHECK no deseadas.

Autogenerate no puede actualmente, pero eventualmente detectará:

  • Las adiciones de restricciones independientes, las eliminaciones, como CHECK , UNIQUE , FOREIGN KEY , aún no están implementadas. En este momento, obtendrá restricciones dentro de las nuevas tablas, restricciones PK y FK para el "downgrade" a una tabla previamente existente, y las restricciones CHECK generadas con un "esquema" SQLAlchemy tipos Boolean , Enum .
  • Adiciones al índice, remociones aún no implementadas.
  • Adiciones de secuencias, eliminaciones, aún no implementadas.

ACTUALIZACIÓN: algunos de los elementos de esta última lista son compatibles con las versiones de Alembic 0.7.x.


Mi error fue intentar crear mi migración inicial con la base de datos ya en el estado final, pensando que notaría que no tenía versiones existentes y basarse en los modelos. Obtuve versiones vacías hasta que borré todas las tablas en la base de datos y luego funcionó bien.


Pruebe flask-alembic https://github.com/tobiasandtobias/flask-alembic

Lo probé ayer. Funciona bien para mí, excepto las operaciones de drop . No funcionan en sqlite ( https://bitbucket.org/zzzeek/alembic/issue/21/column-renames-not-supported-on-sqlite ).

La forma en que lo he usado. Primero utilicé python manage.py migrate revision --autogenerate para crear tablas vacías en sqlite db. Producirá tal migración

def upgrade(): ### commands auto generated by Alembic - please adjust! ### op.create_table(''users_user'', sa.Column(''id'', sa.Integer(), nullable=False), sa.Column(''name'', sa.String(length=50), nullable=True), sa.Column(''email'', sa.String(length=120), nullable=True), sa.Column(''password'', sa.String(length=20), nullable=True), sa.Column(''role'', sa.SmallInteger(), nullable=True), sa.Column(''status'', sa.SmallInteger(), nullable=True), sa.PrimaryKeyConstraint(''id''), sa.UniqueConstraint(''email''), sa.UniqueConstraint(''name'') ) ### end Alembic commands ### def downgrade(): ### commands auto generated by Alembic - please adjust! ### op.drop_table(''users_user'') ### end Alembic commands ###

Entonces - python manage.py migrate upgrade head

Luego agregué una nueva test = db.Column(db.String(20)) columna test = db.Column(db.String(20)) al modelo de Usuario y ejecuté este comando python manage.py migrate revision --autogenerate -m ''test field at users''

Esto produjo tal migración:

def upgrade(): ### commands auto generated by Alembic - please adjust! ### op.add_column(''users_user'', sa.Column(''test'', sa.String(length=20), nullable=True)) ### end Alembic commands ### def downgrade(): ### commands auto generated by Alembic - please adjust! ### op.drop_column(''users_user'', ''test'') ### end Alembic commands ###


Si encuentra la función que necesita, no es compatible con alambique. En ese caso

  1. eliminar la tabla (o columna) y luego migrar

  2. agregar tabla (o columna) de nuevo y luego migrar de nuevo

Esto también funciona en cambio de tipo de enumeración