Skip to main content

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:

This mode mimics Frappe's behavior. Migrations ONLY manage DocTypes from apps specified in the INSTALLED_APPS list.

How to trigger:

  • Use the --apps flag in the CLI:
    m migrate create "added finance" --apps "finance,m_imprest"
  • Or set the INSTALLED_APPS environment 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 TABLE immediately.
  • 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 sync automatically discovers these hooks in all apps listed in --apps.
  • Context: The hooks receive a SQLAlchemy engine for 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/versions folder.
  • 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

ConceptFrappeFramework M
Auto-Syncbench migrate (sync phase)m migrate sync
Manual Patchespatches.txt + Python scriptsm migrate run + Alembic scripts
ModularityDistributed patches.txt in each appDistributed alembic/versions in each app
DependenciesHandled via after_install or global orderHandled via Alembic's depends_on

Commands

CommandDescription
m migrate all(Recommended) Runs both versioned patches (run) and declarative sync.
m migrate syncDirect DocType -> DB synchronization (no files).
m migrate initInitialize Alembic for versioned migrations.
m migrate create "msg" [--app ...]Generate a versioned patch for a specific app.
m migrateAlias for m migrate run. Runs all pending versioned patches.
m migrate statusShow current database revision and pending status.

Zero-Downtime Deployment Strategy (DevOps)

By decoupling sync and run, Framework M enables advanced zero-downtime workflows:

  1. 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.
  2. Stage 2: Code Rollout - Update application instances to the new version.
  3. 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.