Database migrations¶
bluefox-core provides Alembic integration that handles async engine configuration, model discovery, and database URL normalization automatically.
Setup¶
Initialize Alembic in your project:
Replace the generated migrations/env.py with:
from alembic import context
from bluefox_core.migrations import configure_alembic
configure_alembic(context)
That's it. The helper reads your DATABASE_URL from .env, discovers your models, and configures the async engine.
A ready-to-copy template is also available at bluefox_core/migrations/env_template.py.
Model discovery¶
configure_alembic() automatically discovers your models — no configuration required. Just put your models in a models.py file and inherit from BluefoxBase.
Convention (zero config)¶
bluefox-core scans your project root for models.py files in these locations:
models.pyapp/models.py*/models.py(any top-level package)
All matching files are imported automatically. Your models are registered in BluefoxBase.metadata on import, which is how Alembic detects your tables.
# models.py (or app/models.py, myapp/models.py, etc.)
from sqlalchemy.orm import Mapped, mapped_column
from bluefox_core import BluefoxBase
class User(BluefoxBase):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
email: Mapped[str] = mapped_column(unique=True)
name: Mapped[str]
Multiple models.py files across packages are discovered automatically — no need for a central re-export file. For example, both users/models.py and posts/models.py will be found and imported.
Explicit module (override)¶
If your models don't follow the convention, set MODELS_MODULE in your .env:
When set, only this module is imported — convention scanning is skipped.
Inheritance lint check¶
bluefox-core validates that your models inherit from BluefoxBase. If a model inherits from SQLAlchemy's DeclarativeBase directly (or a different base), you'll see a warning:
WARNING Model myapp.models.User inherits from DeclarativeBase but not BluefoxBase.
It won't be tracked by Bluefox migrations.
Change it to inherit from BluefoxBase instead.
This catches a common mistake where models exist but don't show up in migrations because they use the wrong base class.
Creating migrations¶
After changing your models, generate a migration:
This creates a file in migrations/versions/. Review it before applying — autogenerate is good but not perfect.
Running migrations¶
Or using alembic directly for more control:
# Apply one migration forward
uv run alembic upgrade +1
# Roll back to the beginning
uv run alembic downgrade base
Checking status¶
# Show current revision
make migrate-status
# Show migration history
make migrate-history
# Show head revisions
uv run alembic heads
Makefile reference¶
| Command | Description |
|---|---|
make migrate | Apply all pending migrations |
make migrate-make m="description" | Create a new autogenerated migration |
make migrate-down | Roll back one migration |
make migrate-status | Show current migration revision |
make migrate-history | Show full migration history |
Offline mode¶
For generating SQL scripts without a database connection:
configure_alembic() handles offline mode automatically, using DATABASE_URL for the dialect.