Database Migrations
Relevant source files
The following files were used as context for generating this wiki page:
Database migrations in SeaORM Pro provide a structured, versioned approach to managing the database schema. The system utilizes the sea-orm-migration crate to define schema changes in Rust code, allowing for programmatic creation, modification, and rollback of database tables. This ensures that the database state remains consistent across different development, testing, and production environments.
The migration system is designed to handle both structural changes (creating tables and indexes) and data initialization (seeding users and core application data). It integrates with the Loco framework's task runner and the SeaORM CLI to provide a seamless workflow for database lifecycle management.
Sources: README.md, migration/README.md, migration/src/lib.rs
Migrator Architecture
The core of the migration system is the Migrator struct, which implements the MigratorTrait. This struct acts as a registry for all individual migration files, defining the exact order in which they should be applied. Each migration is encapsulated in its own module and represented as a Box<dyn MigrationTrait>.
Migration Execution Flow
When a migration command is issued, the system iterates through the list of registered migrations. For each migration, it checks if it has already been applied to the target database. If not, it executes the up method.
The flow shows how the migrator processes pending scripts to ensure the schema reaches the desired state.
Sources: migration/src/lib.rs:20-35, migration/README.md
Key Components
| Component | Description |
|---|---|
Migrator | The central registry implementing MigratorTrait that defines the sequence of migrations. |
MigrationTrait | Interface requiring up (to apply) and down (to revert) methods. |
SchemaManager | A utility provided by SeaORM to execute DDL statements (Create, Alter, Drop). |
Iden | An enum-based identifier system used to represent table and column names type-safely. |
Sources: migration/src/lib.rs, migration/src/m20250101_000001_user.rs:36-47
Schema Definition
Migrations use a fluent API to define tables, columns, and constraints. This approach avoids raw SQL strings, providing compile-time checks for schema definitions.
Table Construction
Tables are constructed using Table::create(). This includes defining columns with specific types (e.g., date_time, big_integer, string), primary keys, and uniqueness constraints.
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let table = Table::create()
.table(User::Table)
.col(date_time(User::CreatedAt).default(Expr::current_timestamp()))
.col(big_integer(User::Id).auto_increment().primary_key().take())
.col(string_uniq(User::Email))
.col(string(User::Name))
.to_owned();
manager.create_table(table).await?;
Ok(())
}
Sources: migration/src/m20250101_000001_user.rs:10-23
Constraints and Foreign Keys
Relationships between tables are enforced via foreign keys defined within the migration files. For instance, the SalesOrderDetail migration establishes links to both the SalesOrderHeader and Product tables, including on_delete behaviors.
This diagram illustrates the relational structure defined across multiple migration files.
Sources: migration/src/m20241101_000010_sales_order_detail.rs:26-41, migration/src/m20241101_000007_product.rs:34-45
Data Seeding
The project supports two types of seeding: migration-based seeding for essential system data and task-based seeding for large datasets.
Migration-based Seeding
Critical data, such as initial admin users, is handled directly within migration files using the Query::insert() builder. This ensures that a usable system state is created immediately upon schema deployment.
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let insert = Query::insert()
.into_table(User::Table)
.columns([User::Pid, User::Email, User::Name])
.values_panic([Uuid::new_v4().into(), "admin@sea-ql.org".into(), "Admin".into()])
.to_owned();
manager.exec_stmt(insert).await?;
Ok(())
}
Sources: migration/src/m20250101_000002_seed_users.rs:11-30
Task-based Seeding (CSV)
For large-scale data initialization (e.g., product catalogs), the SeedData task implements a CSV import pipeline. It reads files from the migration/AdventureWorks-2012-LT-Script/ directory, transcodes them, and inserts them into the database in chunks.
| Step | Operation | Source File |
|---|---|---|
| 1 | Execute Migration | src/tasks/seed.rs |
| 2 | Read CSV/TSV | src/tasks/seed.rs |
| 3 | Decode Encoding (Win-1252/UTF-16) | src/tasks/seed.rs |
| 4 | Chunked Insertion (100 rows) | src/tasks/seed.rs |
Sources: src/tasks/seed.rs:35-86, src/tasks/seed.rs:104-142
CLI Usage
The migration system is managed through the cargo run -- migrate command (when using the migrator binary) or Loco's task runner.
- Generate:
cargo run -- migrate generate MIGRATION_NAME - Apply:
cargo run -- up - Rollback:
cargo run -- down - Fresh Install:
cargo run -- fresh(Drops all tables and reapplies all migrations) - Status:
cargo run -- status
Sources: migration/README.md:1-35
Summary
The Database Migration system in SeaORM Pro serves as the foundational layer for schema management and data integrity. By utilizing Rust-based definitions via MigratorTrait, it provides a type-safe environment for evolving the database. The dual-approach to seeding—combining migration-based essential data with task-driven bulk imports—allows the project to scale from simple setup to complex demo environments with minimal manual intervention.