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
enEnvironmentContext.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
enEnvironmentContext.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 conENUM
directamente, esto se debe a que la representación de dicho tipo en la base de datos no compatible, es decir, una restricciónCHAR+CHECK
, podría ser cualquier tipo deCHAR+CHECK
. Para SQLAlchemy determinar que esto es realmente unENUM
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 eventosqlalchemy.events.DDLEvents.column_reflect()
para alterar el tipo SQLAlchemy pasado para ciertas columnas y posiblementesqlalchemy.events.DDLEvents.after_parent_attach()
para interceptar restriccionesCHECK
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 restriccionesCHECK
generadas con un "esquema" SQLAlchemy tiposBoolean
,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
eliminar la tabla (o columna) y luego migrar
agregar tabla (o columna) de nuevo y luego migrar de nuevo
Esto también funciona en cambio de tipo de enumeración