Skip to main content
Call User::migrate(&pool).await? at startup to create or upgrade its table. Rydantic compares the live SQLite, PostgreSQL or MySQL catalog with SqlModel metadata, generates the upgrade, executes it, and verifies the resulting schema. Tables previously created by create_table can be upgraded directly. Matching schemas produce a no-op receipt without adding history rows.
Back up existing databases, inspect the migration plan, and test the upgrade on a copy before deployment. Application owners remain responsible for existing data, custom SQL objects and deployment validation. Destructive opt-in permits planned data loss; it does not make a migration lossless or override unsupported-schema guards. Unsupported layouts need a reviewed manual migration, not removal of constraints to bypass a rejection.
For example, evolve an existing accounts(id, name) model:
The rename preserves existing values. Old rows receive enabled = true and note = NULL. On MySQL, keep compatible length bounds on the earlier model so adding an index does not also narrow LONGTEXT to VARCHAR. Storage narrowing requires the destructive opt-in below. SQL defaults accept string, boolean, signed 64-bit integer, and finite float literals matching the column storage type. They remain database defaults for future SQL inserts that omit the field. Model parsing defaults and factories are configured independently with #[model(default = ...)]. Making an optional field required also backfills existing NULL values from its SQL default. Adding a required column to a populated table without a SQL default returns migration_data before changing its schema. Preview and apply an exact plan:
apply checks the catalog again under the migration lock and returns migration_stale if it changed after preview. Plans expose immutable statements and changes. migrate plans under the lock to serialize concurrent startups. These operations accept pools so they can manage their own connection and transaction lifecycle. After inspecting a destructive plan, enable its changes for that invocation:
Storage conversions use database casts and constraints. SQLite checks existing storage classes before type-changing rebuilds and rejects incompatible values. The opt-in permits planned data loss; custom data transformations require separate application logic. Primary-key/identity strategy changes, composite keys/indexes, generated columns and custom foreign-key semantics require an explicit migration. Table renames are outside this per-table API. Apply referenced models before dependent models, and coordinate changes to referenced key types separately. SQLite uses transactional rebuilds when needed, preserves auto-increment high-water marks and dependent rows, checks foreign-key integrity before commit, and restores the connection’s foreign-key setting. Rebuilds reject external triggers, views, CHECK constraints and custom storage options whose definitions are outside the model. Since 0.7.0-beta.1, SQLite rebuilds also reject non-BINARY index collations (such as COLLATE NOCASE) and descending index keys, including primary-key indexes. The planner returns migration_unsupported instead of silently recreating these semantics incorrectly. Ordinary BINARY ascending indexes remain supported. A matching no-op or a native rename-only plan does not require a rebuild and is not rejected by this rebuild guard; other validation and database restrictions still apply. See the beta upgrade notes and the implemented index-safety fix. PostgreSQL uses a transaction, advisory lock and table lock; schema and history roll back together on failure. MySQL uses a database-scoped advisory lock and requires strict SQL mode. Structural changes are grouped into one ALTER TABLE where possible. MySQL DDL implicitly commits: a sequence containing a NULL backfill or separate creation statements can leave completed work after a later failure. History records applying, applied or failed; interruption can leave an applying entry. After correcting the cause, retry migrate, which computes remaining work from the live catalog. See MySQL’s DDL transaction rules. _rydantic_migrations stores per-table revisions, SHA-256 target checksums, source/target schema snapshots, change summaries and timestamps. SQLite/PostgreSQL record successful upgrades; MySQL also retains failed attempts. Snapshots include SQL defaults. Automatic upgrades cover SQL metadata; application validation rules continue to run through model parsing and CRUD. Run the complete old-model-to-new-model example: