Move migration logic to alembic env.py and add CLI wrappers
MA
17m ago+1068+1068+0123456789012345678901234567890123456789-844-844-012345678901234567890123456789
Type of change
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
- This change requires a documentation update
Description
This PR refactors the database migration system to move logic out of reflex.model into a generated alembic/env.py template, and provides thin CLI wrappers in reflex.utils.migrations for the reflex db commands.
Key changes:
-
New
reflex/.templates/alembic/env.py: A template thatreflex db initinstalls, which:- Loads the app to register all models
- Reads
db_urlfromrxconfig.pyinstead ofalembic.ini - Handles column defaults (callable and scalar) via a rewriter
- Skips writing empty autogenerated revisions
- Imports
sqlmodelwhen needed
-
New
reflex/utils/migrations.py: Thin wrappers over alembic's CLI:run(): Execute alembic commands with proper error handlinginit(): Runalembic initand install the reflex templatescript_directory(): Locate the alembic scripts directoryis_legacy_env(): Detect stock alembic templates from older releasesupdate_env(): Replace legacy env.py with the reflex templatedescribe_error(): Format alembic errors for display
-
Refactored
reflex/reflex.py(db_clicommands):db init: Now callsmigrations.init()and applies the initial revisiondb migrate: Callsmigrations.run("upgrade", "head")db makemigrations: Callsmigrations.run("revision", "--autogenerate", ...)db status: Callsmigrations.run("history", ...)- Added
_alembic_errors()context manager to catch and reportCommandError - Added logic to detect and update legacy migration environments
-
Removed from
reflex/model.py:alembic_autogenerate(): Logic moved to env.py's rewriterget_migration_history(): No longer needed;alembic historyhandles itformat_revision(): Moved to reflex.py fordb status_alembic_config(),_alembic_render_item(),_alembic_upgrade(): Integrated into env.py- Simplified
alembic_init()to just callmigrations.init() - Simplified
migrate()to callmigrations.run()
-
Updated
reflex/utils/prerequisites.py:check_db_initialized(): Now also checks for legacy env.pycheck_schema_up_to_date(): Refactored to use alembic's native commands
-
Comprehensive test suite in
tests/units/utils/test_migrations.py:- Tests for both SQLModel and SQLAlchemy ORMs
- Tests for schema evolution (add/drop columns, add tables)
- Tests for callable defaults being evaluated for existing rows
- Tests for UTC datetime migrations
- Tests for legacy environment detection and update
- Tests for error reporting and initialization checks
Breaking Changes
The following functions are removed from the public API:
reflex.model.alembic_autogenerate()reflex.model.get_migration_history()reflex.model.format_revision()
These were internal implementation details; the reflex db CLI remains the public interface.
Test Plan
- Added 511 lines of comprehensive tests in
tests/units/utils/test_migrations.pycovering:- Both SQLModel and SQLAlchemy ORMs
- Schema evolution scenarios (add/drop columns and tables)
- Callable defaults evaluation