db:migrate
Table of contents
- Introduction
- Writing a migration by hand
- Migrating a refiner
- Pinned features
- Reference
- Requirements
- See also
Introduction
db:migrate writes Alembic migration scripts from your model changes, for every editable feature or for one. It never applies them. Review the files, then run db:upgrade.
splent db:migrate splent_feature_notes -m "add a pinned flag to notes"
📝 splent_feature_notes: new migration generated
Without a feature it visits every feature in the UVL dependency order that feature:order shows, and prints one line each. A feature with no schema change says âś” <feature>: up to date, and the empty script is deleted.
A feature can be named notes, splent_feature_notes, splent_feature_notes@v1.2.0 or splent-io/splent_feature_notes@v1.2.0.
Writing a migration by hand
splent db:migrate splent_feature_notes --empty -m "backfill note titles"
📝 splent_feature_notes: blank migration generated — edit upgrade()/downgrade() and run 'splent db:upgrade'
--empty writes a blank migration and keeps it, for a data migration or a change autogenerate cannot see. It needs a feature.
New scripts are formatted with ruff format when ruff is installed, so the release’s lint accepts them, and they belong to the owner of the folder they land in, so you can edit them on the host.
Migrating a refiner
A refiner adds columns to its base feature’s table through a mixin. Each feature’s migrations only ever write its own columns.
| Run on | What autogenerate writes |
|---|---|
| The base feature | Its own columns. It leaves out every refiner column, with its indexes, unique constraints and foreign keys |
| A refiner | The columns its mixins inject into the base table, with their indexes and foreign keys, plus the refiner’s own tables |
The base table has to exist first, so upgrade the base before you migrate the refiner.
splent db:upgrade splent_feature_notes
splent db:migrate splent_feature_notes_tags -m "add tags column to notes"
To remove a column from a mixin, write its drop_column by hand in the refiner’s migration. The base’s next db:migrate would then drop it too, so take that drop out of the base’s migration. Tutorial 6 (Extend with refinement) walks through the whole case.
Pinned features
A pinned feature is skipped with skipping <feature> (pinned, run feature:unlock to generate migrations for it). Its migrations ship with its release. Run feature:unlock to edit it.
Reference
Options
| Option | Description |
|---|---|
FEATURE |
Optional. Only this feature |
-m MESSAGE |
The migration message. The feature name by default |
--empty |
Write a blank migration and keep it. Needs FEATURE |
Errors
| Message | What to do |
|---|---|
❌ <feature>: migration generation failed (<alembic error>) |
Fix what alembic reports. The other features still run, and the command ends with Error: Migration generation failed for: <features> and exit 1 |
❌ No migrations directory found for '<feature>'. |
The feature has no migrations/ folder |
❌ --empty requires a FEATURE argument. |
Name the feature |
Error: Cannot read the features of <product>/pyproject.toml: <reason> |
Fix the TOML, or the feature list it names |
Requirements
- A selected product (
SPLENT_APP), and a reachable database. - A
migrations/folder in the feature. Per-feature migration system describes it.
See also
db:statusshows the migration status of each feature.db:rollbackrolls back migrations.db:resetdrops the tables and migrates again.