Manage database schema changes with Flask-Migrate and Alembic
The Soft-Bee API uses Flask-Migrate (built on Alembic) to manage database schema changes. This ensures version-controlled, reproducible database migrations across all environments.
Ensure your .env file has the correct database URL:
# Local developmentDATABASE_URL=postgresql://postgres:password@localhost:5432/softbee_local# TestingDATABASE_URL=postgresql://postgres:password@localhost:5432/softbee_test# ProductionDATABASE_URL=postgresql://user:password@host:port/database
The configuration is loaded from config.py:41-88 based on FLASK_ENV.
3
Import All Models
Ensure all SQLAlchemy models are imported in db.py so Flask-Migrate can detect them:
def init_app(app): # ... with app.app_context(): # Import all models here from src.features.auth.infrastructure.models.user_model import UserModel from src.features.apiary.infrastructure.models.apiary_model import ApiaryModel # Import other models as you create them
# Goodflask db migrate -m "Add email verification fields to user model"flask db migrate -m "Create apiary and hive relationship tables"# Badflask db migrate -m "Update database"flask db migrate -m "Changes"
Test Migrations Both Ways
Test both upgrade and downgrade:
# Apply migrationflask db upgrade# Verify database state# ...# Rollback migrationflask db downgrade# Verify rollback worked# ...# Apply againflask db upgrade
Handle Data During Schema Changes
When modifying columns, preserve existing data:
def upgrade(): # Add new column as nullable first op.add_column('users', sa.Column('full_name', sa.String(200), nullable=True)) # Populate with existing data op.execute("UPDATE users SET full_name = first_name || ' ' || last_name") # Make it non-nullable op.alter_column('users', 'full_name', nullable=False)
Use Batch Operations for Large Tables
For tables with many rows, use batch operations:
def upgrade(): with op.batch_alter_table('large_table') as batch_op: batch_op.add_column(sa.Column('new_field', sa.String(50))) batch_op.create_index('ix_large_table_new_field', ['new_field'])
# Use test databaseexport FLASK_ENV=testing# Apply migrations before testsflask db upgrade# Run testspytest# Clean up (optional)flask db downgrade base
# Drop all tablesflask db downgrade base# Or manuallypsql -U postgres -d softbee_localDROP SCHEMA public CASCADE;CREATE SCHEMA public;\q# Recreate from scratchflask db upgrade
# Check current stateflask db current# Manual fix might be neededpsql -U postgres -d your_database# Fix the issue manually# Mark migration as complete (if you fixed it manually)flask db stamp head# Or rollback and fix the migration scriptflask db downgrade# Edit the migration fileflask db upgrade