docs: add complete alembic integration example - #1741
Conversation
Expand the migrations/Alembic docs with a working, end-to-end example: - Explain where the SQLAlchemy MetaData comes from in ormar and how to expose it for Alembic. - Add a sample project layout with my_project/models.py, alembic.ini, and alembic/env.py. - Show how to handle models split across multiple apps/packages by importing all models before Alembic reads the metadata. - Keep the existing compare_type and include_object tips intact. Fixes ormar-orm#1400
eecaa52 to
393859e
Compare
| and not an Engine, though an Engine is acceptable | ||
| here as well. By skipping the Engine creation | ||
| we don't even need a DBAPI to be available. | ||
| `alembic.ini` (only the parts you typically need to change): |
There was a problem hiding this comment.
Copying this file together with the env.py below gives:
File ".../logging/config.py", line 115, in _create_formatters
flist = cp["formatters"]["keys"]
KeyError: 'formatters'
env.py calls fileConfig(config.config_file_name), which needs [loggers], [handlers] and [formatters] — none of which are here.
The mixed framing is what causes it. The heading says "Complete project layout", the intro says "minimal but complete layout that works with alembic revision --autogenerate", and env.py is given in full as a drop-in replacement — so it's natural to read this file the same way. But the caption says "only the parts you typically need to change".
Either include the generated logging sections so the file is genuinely complete, or reword to something unambiguous: "In the generated alembic.ini, change only these keys — leave the rest, including the logging sections, as-is."
For what it's worth, with the logging sections restored both layouts work end to end: autogenerate detects authors and books, and alembic upgrade head creates them. So the substance is right, it's just the ini that's under-specified.
| """ | ||
| prepend_sys_path = . | ||
|
|
||
| sqlalchemy.url = sqlite:///db.sqlite |
There was a problem hiding this comment.
This is correct, but the doc never says why it differs from the sqlite+aiosqlite:///db.sqlite the models use a few blocks up. Given the audience the issue describes, someone will "fix" the inconsistency and land on:
sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called;
can't call await_only() here.
which is not a message you can search your way out of. Could you add a short callout right here — ormar talks to the database through an async driver, alembic's default env.py is synchronous, so the two URLs point at the same database through different drivers on purpose. A small table would carry a lot of weight:
| database | ormar (DatabaseConnection) |
alembic (sqlalchemy.url) |
|---|---|---|
| SQLite | sqlite+aiosqlite:///db.sqlite |
sqlite:///db.sqlite |
| PostgreSQL | postgresql+asyncpg://... |
postgresql+psycopg2://... |
| MySQL | mysql+aiomysql://... |
mysql+pymysql://... |
And one line pointing at alembic init -t async for anyone who'd rather keep a single URL.
| # for 'autogenerate' support | ||
| from app.models.my_models import metadata | ||
| target_metadata = metadata | ||
| `my_project/models.py`: |
There was a problem hiding this comment.
We keep runnable doc examples in docs_src/ and pull them into the markdown with the snippet syntax — see docs/signals.md:38 for the pattern. docs_src/test_all_docs.py executes every .py under that tree as a script in CI, so those examples can't silently rot.
Could these two model modules move to e.g. docs_src/models/docs0NN.py and be included with:
--8<-- "../docs_src/models/docs0NN.py"
scripts/test_docs.sh only runs pytest docs_src/ — markdown code fences are never executed, which is exactly how the alembic.ini problem above got through a green CI run. env.py and alembic.ini obviously can't live there; inline is fine for those.
| ``` | ||
|
|
||
| ### Sample env.py file | ||
| ### Where does `metadata` come from? |
There was a problem hiding this comment.
The Author/Book definitions here are byte-identical to the ones in "Complete project layout" ~70 lines down. The # env.py snippet in this section also imports from my_project.models import metadata before my_project has been introduced anywhere.
Suggest trimming this section to the prose plus the one-liner it already has:
target_metadata = Author.ormar_config.metadataand letting "Complete project layout" carry the single full listing. The explanation stands on its own without 30 lines of models repeated.
| # the prefix has to match sqlalchemy import name in alembic | ||
| # that can be set by sqlalchemy_module_prefix option (default 'sa.') | ||
| user_module_prefix='sa.' | ||
| # Required if you use ormar.UUID(). |
There was a problem hiding this comment.
The old comment carried the part that's actually actionable: the prefix has to match the SQLAlchemy import name used in the generated migration, which alembic exposes as sqlalchemy_module_prefix (default sa.). Anyone who changed that setting now has no way to know what belongs here. Could you keep a condensed version of the why?
Unrelated but adjacent: the compare_type section further down still has 'sa.' in single quotes while everything new uses double — worth normalizing since you're already touching quoting in this file.
| script_location = %(here)s/alembic | ||
|
|
||
| """ | ||
| prepend_sys_path = . |
There was a problem hiding this comment.
This and the sys.path.insert(0, str(Path(__file__).resolve().parents[1])) in env.py do the same job. Having both is confusing for exactly the reader this doc is aimed at. I'd keep the env.py one — it works regardless of which directory alembic is invoked from — and drop this line.
Related: both sqlalchemy.url and the ormar URL are cwd-relative, so db.sqlite only lands at the project root shown in the tree if you always run from the root. sqlite:///%(here)s/db.sqlite pins it.
| alembic upgrade head | ||
| ``` | ||
|
|
||
| ### Multiple apps with models |
There was a problem hiding this comment.
authors/ and books/ have no __init__.py here, while my_project/ and models/ do. It works via namespace packages (I checked), but the inconsistency reads as an oversight in a doc whose entire purpose is leaving nothing implicit.
Also: the single-file env.py puts from my_project.models import metadata after sys.path.insert, which is E402 under most linters. The multi-app snippet carries # noqa: F401 but the single-file one has nothing — consider # noqa: E402 on both so people don't paste a lint error into their project.
Fixes #1400.
The current Alembic docs skip too many steps for anyone new to ormar — imports, where
metadatacomes from, and how to handle models split across apps. I added a complete, working example based on the FastAPI-template reference linked in the issue.It covers a single-file layout, a multi-app layout, a minimal
alembic.ini, and wiring the sharedmetadataintoalembic/env.py. I ranalembic revision --autogenerateandalembic upgrade headagainst both layouts and built the docs withmkdocs buildto make sure everything works.