Database Migrations
Framework M uses Alembic to manage database migrations. It supports both automatic discovery of DocTypes and selective migration based on "installed apps".
Migration Anatomy
Migrations are stored in the host application's directory (e.g., apps/business-m/alembic/).
alembic/env.py: Orchestrates how DocTypes are discovered and how MetaData is built.alembic/versions/: Contains the generated migration scripts.alembic.ini: Configuration file for Alembic.
Discovery Strategies
Framework M supports two modes of DocType discovery for migrations:
1. Selective Discovery (Recommended for Production)
This mode mimics Frappe's behavior. Migrations ONLY manage DocTypes from apps specified in the INSTALLED_APPS list.
How to trigger:
- Use the
--appsflag in the CLI:m migrate create "added finance" --apps "finance,m_imprest" - Or set the
INSTALLED_APPSenvironment variable:export INSTALLED_APPS="finance,wms"
m migrate status
How it works:
The env.py script calls MetaRegistry.load_apps(apps), which imports the specified packages and registers their DocTypes. Tables not in these apps are ignored by the migration generator.
2. Monolithic Discovery (Development Default)
If no INSTALLED_APPS or --apps are provided, the system falls back to a recursive filesystem scan of the src/ folder.
How it works:
The system crawls all directories named doctypes within the workspace and automatically registers every DocType it finds.
Sync vs. Migrate (The Dual-Track Approach)
To provide a Frappe-like "it just works" experience while maintaining production-grade control, Framework M uses a dual-track strategy:
1. The "Sync" Track (Declarative / Frappe-style)
Use m migrate sync when you want the database to exactly match your current DocType definitions.
- Behavior: Compares current DocTypes with the DB schema and applies
CREATE TABLE/ALTER TABLEimmediately. - Self-Contained: No migration files are created. It is entirely driven by code.
- Use Case: Rapid development, local prototyping, and "Syncing" schema stability.
1.1 Sync Hooks (The Lifecycle Track)
To handle complex data transitions during a sync, each app can define hooks in its migrations module:
File: libs/wms/src/wms/migrations.py
def before_sync(engine):
"""Run before DDL sync (e.g. drop old constraints)"""
pass
def after_sync(engine):
"""Run after DDL sync (e.g. initialize default data)"""
pass
- Execution:
m migrate syncautomatically discovers these hooks in all apps listed in--apps. - Context: The hooks receive a SQLAlchemy
enginefor direct data manipulation.
2. The "Migrate" Track (Imperative / Alembic)
Use the standard commands (create, run) when you need to ship ordered patches or logic-heavy transitions (data migrations, complex index changes).
- Behavior: Uses version files inside each app's
alembic/versionsfolder. - Self-Contained: Each app maintains its own version history.
- Use Case: Production rollouts, data migrations, and multi-tenant patches.
m migrate create "v1 patch" --app "wms"
m migrate
Comparisons with Frappe
| Concept | Frappe | Framework M |
|---|---|---|
| Auto-Sync | bench migrate (sync phase) | m migrate sync |
| Manual Patches | patches.txt + Python scripts | m migrate run + Alembic scripts |
| Modularity | Distributed patches.txt in each app | Distributed alembic/versions in each app |
| Dependencies | Handled via after_install or global order | Handled via Alembic's depends_on |
Commands
| Command | Description |
|---|---|
m migrate all | (Recommended) Runs both versioned patches (run) and declarative sync. |
m migrate sync | Direct DocType -> DB synchronization (no files). |
m migrate init | Initialize Alembic for versioned migrations. |
m migrate create "msg" [--app ...] | Generate a versioned patch for a specific app. |
m migrate | Alias for m migrate run. Runs all pending versioned patches. |
m migrate status | Show current database revision and pending status. |
Zero-Downtime Deployment Strategy (DevOps)
By decoupling sync and run, Framework M enables advanced zero-downtime workflows:
- Stage 1: Safe Sync - Run
m migrate sync(declarative) before updating application code. This adds new nullable columns or tables that don't break existing code. - Stage 2: Code Rollout - Update application instances to the new version.
- Stage 3: Data Patches - Run
m migrate run(imperative) to perform data transformations or breaking schema changes once the new code is fully operational.
Tips for Monorepos
In a monorepo, it is best to run migrations from the main app (apps/business-m) but specify which libs should be included in the schema using the --apps flag.