Skip to content

Commit e78e1bf

Browse files
authored
Merge pull request #39 from borhanst/alembic-migration
Alembic migration
2 parents b78aff1 + 5a2bb56 commit e78e1bf

38 files changed

Lines changed: 1383 additions & 411 deletions

docs/guide/alembic-setup.md

Lines changed: 198 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,95 @@
11
# Alembic Setup
22

3-
FastAPI Admin Kit does not bundle Alembic. This guide shows how to set up Alembic in your project to manage database migrations for both your models and the admin tables.
3+
FastAPI Admin Kit provides built-in models for authentication (users, roles, permissions) and audit logging. This guide shows how to set up Alembic to manage database migrations for both admin tables and your application models.
44

5-
## Install Alembic
5+
## Quick Start with `fak init-alembic`
6+
7+
The easiest way to get started is using the built-in CLI command:
68

79
```bash
8-
pip install alembic
10+
# For a new project
11+
fak init-alembic --app myapp:app --auto-migrate
12+
13+
# For an existing database (baseline migration)
14+
fak init-alembic --app myapp:app --baseline
915
```
1016

11-
## Initialize Alembic
17+
This command:
18+
1. Creates `alembic.ini` with proper configuration
19+
2. Creates `alembic/env.py` that imports admin models from `fastapi_admin_kit.migrations.models`
20+
3. Creates `alembic/script.py.mako` template
21+
4. Optionally auto-generates the initial migration (`--auto-migrate`)
22+
5. Optionally creates a baseline migration for existing databases (`--baseline`)
23+
24+
### What `init-alembic` Does
1225

13-
```bash
14-
alembic init alembic
26+
```
27+
myproject/
28+
├── alembic.ini # Alembic configuration
29+
├── alembic/
30+
│ ├── env.py # Migration environment (imports admin models)
31+
│ ├── script.py.mako # Migration template
32+
│ └── versions/ # Migration scripts
1533
```
1634

17-
## Configure `alembic.ini`
35+
The generated `alembic/env.py` includes:
1836

19-
Set the database URL:
37+
```python
38+
# Import admin models (materialized from schemas)
39+
from fastapi_admin_kit.migrations.models import Base as AdminBase
2040

21-
```ini
22-
[alembic]
23-
script_location = alembic
24-
sqlalchemy.url = sqlite+aiosqlite:///./your_database.db
41+
# Import your app models
42+
# from myapp.models import Base as AppBase
43+
44+
# Combine metadata for autogenerate
45+
target_metadata = [AdminBase.metadata]
46+
# target_metadata.append(AppBase.metadata) # Add your models
2547
```
2648

27-
For PostgreSQL:
49+
## Manual Alembic Setup
2850

29-
```ini
30-
sqlalchemy.url = postgresql+asyncpg://user:password@localhost:5432/your_database
51+
If you prefer manual setup or have an existing Alembic configuration:
52+
53+
### 1. Install Alembic
54+
55+
```bash
56+
pip install alembic
57+
```
58+
59+
### 2. Initialize Alembic
60+
61+
```bash
62+
alembic init alembic
3163
```
3264

33-
## Configure `alembic/env.py`
65+
### 3. Configure `alembic/env.py`
3466

3567
Replace the contents of `alembic/env.py`:
3668

3769
```python
3870
import asyncio
3971
import sys
40-
from pathlib import Path
4172
from logging.config import fileConfig
73+
from pathlib import Path
4274

4375
from sqlalchemy import pool
4476
from sqlalchemy.engine import Connection
4577
from sqlalchemy.ext.asyncio import async_engine_from_config
4678

4779
from alembic import context
4880

81+
# Add project root to path
4982
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
5083

51-
from models import Base as UserBase
52-
from fastapi_admin_kit.models.base import Base as AdminBase
84+
# Import admin models (materialized from schemas)
85+
from fastapi_admin_kit.migrations.models import Base as AdminBase
5386

54-
target_metadata = UserBase.metadata
55-
for table in AdminBase.metadata.tables.values():
56-
if table.name not in target_metadata.tables:
57-
target_metadata._add_table(table.name, table.schema, table)
87+
# Import your application models
88+
# from myapp.models import Base as AppBase
89+
90+
# Combine metadata for autogenerate
91+
target_metadata = [AdminBase.metadata]
92+
# target_metadata.append(AppBase.metadata) # Add your models
5893

5994
config = context.config
6095

@@ -101,63 +136,174 @@ else:
101136
run_migrations_online()
102137
```
103138

104-
Adjust the import to match your project:
139+
### 4. Configure Database URL
105140

106-
```python
107-
# If models are in app/models.py
108-
from app.models import Base as UserBase
141+
Edit `alembic.ini`:
109142

110-
# If models are in src/models/__init__.py
111-
from src.models import Base as UserBase
143+
```ini
144+
[alembic]
145+
script_location = alembic
146+
sqlalchemy.url = sqlite+aiosqlite:///./your_database.db
147+
# For PostgreSQL:
148+
# sqlalchemy.url = postgresql+asyncpg://user:pass@localhost:5432/dbname
112149
```
113150

114-
## Generate Initial Migration
151+
### 5. Generate Initial Migration
115152

116153
```bash
117-
alembic revision --autogenerate -m "initial schema"
154+
alembic revision --autogenerate -m "init admin models"
118155
```
119156

120-
## Apply Migration
157+
### 6. Apply Migrations
121158

122159
```bash
123160
alembic upgrade head
124161
```
125162

126-
## Sync Engine
163+
## Production vs Development Mode
164+
165+
FastAPI Admin Kit supports two modes:
166+
167+
| Mode | Setting | Behavior |
168+
|------|---------|----------|
169+
| **Development** (default) | `use_alembic=False` | Uses `create_all()` + auto-migration (adds missing columns) |
170+
| **Production** | `use_alembic=True` | Expects Alembic to manage schema; skips `create_all()` |
127171

128-
For synchronous engines, replace `run_migrations_online`:
172+
### In Your Application
129173

130174
```python
131-
from sqlalchemy import engine_from_config
175+
from fastapi_admin_kit import Admin
176+
from fastapi_admin_kit.config import BehaviorConfig
177+
178+
admin = Admin(
179+
app=app,
180+
engine=engine,
181+
# ... other config ...
182+
behavior=BehaviorConfig(use_alembic=True), # Production mode
183+
)
184+
```
132185

133-
def run_migrations_online() -> None:
134-
connectable = engine_from_config(
135-
config.get_section(config.config_ini_section, {}),
136-
prefix="sqlalchemy.",
137-
poolclass=pool.NullPool,
138-
)
139-
with connectable.connect() as connection:
140-
context.configure(connection=connection, target_metadata=target_metadata)
141-
with context.begin_transaction():
142-
context.run_migrations()
186+
### In Lifespan (Production)
187+
188+
```python
189+
@asynccontextmanager
190+
async def lifespan(app: FastAPI):
191+
# Run alembic upgrade head on startup
192+
from alembic.config import Config
193+
from alembic import command
194+
195+
alembic_cfg = Config("alembic.ini")
196+
command.upgrade(alembic_cfg, "head")
197+
198+
await admin.setup(app)
199+
yield
143200
```
144201

145-
## Existing Tables
202+
## CLI Commands
146203

147-
If tables were created with `create_all()` before Alembic:
204+
### Initialize Alembic
148205

149206
```bash
207+
# New project with auto-generated initial migration
208+
fak init-alembic --app myapp:app --auto-migrate
209+
210+
# Existing project with database (creates baseline)
211+
fak init-alembic --app myapp:app --baseline
212+
213+
# Force overwrite existing alembic config
214+
fak init-alembic --app myapp:app --force
215+
```
216+
217+
### Run Migrations (Production)
218+
219+
```bash
220+
# Run all pending migrations (equivalent to alembic upgrade head)
221+
fak migrate-alembic
222+
223+
# Run to specific revision
224+
fak migrate-alembic <revision>
225+
226+
# Use custom app path to find alembic.ini
227+
fak migrate-alembic --app myapp:app
228+
```
229+
230+
### Dev Mode Migrations (Legacy)
231+
232+
```bash
233+
# Add missing columns / recreate tables (dev only)
234+
fak migrate User Product
235+
236+
# Convert old permissions format
237+
fak migrate-permissions
238+
```
239+
240+
## Existing Database Migration (Baseline)
241+
242+
If you have an existing database created with `create_all()`:
243+
244+
```bash
245+
# Create baseline migration and stamp as applied
246+
fak init-alembic --app myapp:app --baseline
247+
248+
# Or manually:
249+
alembic revision -m "baseline_existing_schema"
250+
# Edit the migration to match your current schema
150251
alembic stamp head
151252
```
152253

153-
## Verify Admin Tables
254+
## Adding Your Models to Migrations
255+
256+
In `alembic/env.py`, add your application's metadata:
154257

155258
```python
156-
from fastapi_admin_kit.models.base import Base as AdminBase
157-
print(list(AdminBase.metadata.tables.keys()))
259+
from fastapi_admin_kit.migrations.models import Base as AdminBase
260+
from myapp.models import Base as AppBase # Your models
261+
262+
target_metadata = [AdminBase.metadata, AppBase.metadata]
158263
```
159264

265+
Then autogenerate will include both admin and app tables:
266+
267+
```bash
268+
alembic revision --autogenerate -m "add product table"
269+
```
270+
271+
## Junction Tables
272+
273+
The admin models include two junction tables for many-to-many relationships:
274+
- `admin_user_roles` — User ↔ Role
275+
- `admin_role_permissions` — Role ↔ Permission
276+
277+
These are automatically created via SQLAlchemy relationships and included in migrations.
278+
279+
## Troubleshooting
280+
281+
### "Table already exists" on initial migration
282+
283+
If tables were created via `create_all()` before Alembic:
284+
285+
```bash
286+
# Option 1: Baseline (recommended)
287+
fak init-alembic --baseline
288+
289+
# Option 2: Stamp head manually
290+
alembic stamp head
291+
```
292+
293+
### Import errors in `env.py`
294+
295+
Ensure your project root is in `sys.path`:
296+
297+
```python
298+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
299+
```
300+
301+
### Async engine issues
302+
303+
The generated `env.py` uses `async_engine_from_config` for async migrations. For sync engines, use the sync variant in the template.
304+
160305
## Next Steps
161306

162-
- [Model Registration](model-registration.md)
163-
- [Authentication & RBAC](auth-rbac.md)
307+
- [Model Registration](../guide/model-registration.md) — Register your models with the admin
308+
- [Authentication & RBAC](../guide/auth-rbac.md) — Set up roles and permissions
309+
- [Existing Alembic Integration](./existing-alembic-integration.md) — Integrate with existing Alembic setup

docs/guide/cli.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ fak deletepermissions User Product
110110

111111
### migrate
112112

113-
Add missing columns or drop obsolete columns from tables:
113+
Add missing columns or drop obsolete columns from tables (dev mode only):
114114

115115
```bash
116116
fak migrate User Product
@@ -122,6 +122,52 @@ fak-admin migrate User Product
122122
| `tables` | Class or table names (required) |
123123
| `-d, --database-url` | Database URL |
124124

125+
### migrate-alembic
126+
127+
Run Alembic migrations (production mode):
128+
129+
```bash
130+
# Run all pending migrations (equivalent to alembic upgrade head)
131+
fak migrate-alembic
132+
133+
# Run to specific revision
134+
fak migrate-alembic <revision>
135+
136+
# Use custom app path to find alembic.ini
137+
fak migrate-alembic --app myapp:app
138+
```
139+
140+
| Option | Description |
141+
|--------|-------------|
142+
| `revision` | Target revision (default: `head`) |
143+
| `-a, --app` | App module path (e.g., `myapp:app`) to locate alembic.ini |
144+
145+
### init-alembic
146+
147+
Initialize Alembic configuration for migrations:
148+
149+
```bash
150+
# New project with auto-generated initial migration
151+
fak init-alembic --app myapp:app --auto-migrate
152+
153+
# Existing project with database (creates baseline migration)
154+
fak init-alembic --app myapp:app --baseline
155+
156+
# Force overwrite existing alembic config
157+
fak init-alembic --app myapp:app --force
158+
159+
# Custom database URL
160+
fak init-alembic --app myapp:app -d "postgresql+asyncpg://user:pass@localhost/db"
161+
```
162+
163+
| Option | Description |
164+
|--------|-------------|
165+
| `-a, --app` | App module path (e.g., `myapp:app`) to locate project root |
166+
| `-d, --database-url` | Database URL to write to alembic.ini |
167+
| `--auto-migrate` | Auto-generate initial migration for admin models |
168+
| `--baseline` | Create baseline migration for existing database (stamps head) |
169+
| `--force` | Overwrite existing alembic.ini and alembic/ directory |
170+
125171
### migrate-permissions
126172

127173
Convert old shared permissions to per-role format:

0 commit comments

Comments
 (0)