33

I want to use alembic revision --autogenerate with my own model classes. Because of that I need to import them in myproject/alembic/env.py as described in the docs. But this doesn't work even if I tried a lot of variations.

I am not sure in which context (don't know if this is the correct word) does alembic run the env.py. Maybe that causes some errors.

This is the directory and file structure I use.

myproject/
    common/
        __init__.py
        model.py
    alembic/
        env.py

The error is kind of that

    from .common import model
SystemError: Parent module '' not loaded, cannot perform relative import

myproject itself is just a repository/working directory. It is not installed into the system (with pip3, apt-get, easyinstall or anything else).

buhtz
  • 10,774
  • 18
  • 76
  • 149

4 Answers4

31

You can set the PYTHONPATH environment variable to control what python sees as the top level folder, eg. if you are in the root folder of your project:

PYTHONPATH=. alembic revision -m "..."

Then you can use a "normal" import in your alembic env.py, relative to your root folder, in your example:

from src.models.base import Base
K. Norbert
  • 10,494
  • 5
  • 49
  • 48
21

Fiddling around few hours with this same issue, I found out a solution. First, this is my structure right now:

. ← That's the root directory of my project
├── alembic.ini
├── dev-requirements.txt
├── requirements.txt
├── runtime.txt
├── setup.cfg
├── src
│   └── models
│       ├── base.py
│       ...
│       └── migrations
│           ├── env.py
│           ├── README
│           ├── script.py.mako
│           └── versions
│          
└── tests

in env.py I simply did this:

import sys
from os.path import abspath, dirname
sys.path.insert(0, dirname(dirname(dirname(abspath(__file__))))) # Insert <.>/src
import models # now it can be imported
target_metadata = models.base.Base.metadata

Hope you find this useful! :)

EDIT: I then did my first revision with the database empty (with no tables yet), alembic filled everything automatically for upgrade() and downgrade(). I did that in this way because not all my tables were automagically detected by alembic.

shackra
  • 277
  • 3
  • 16
  • 56
  • Side Note: You will need to adjust your alembic.init `script_location` to your new migrations folder path – Cobalt Jul 23 '20 at 21:09
17

Put this in your env.py to put the working directory onto the Python path:

import sys
import os

sys.path.insert(0, os.getcwd())
Joe Heffer
  • 379
  • 5
  • 6
  • Didn't work for me. I used `sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))` from this tutorial - https://www.learndatasci.com/tutorials/using-databases-python-postgres-sqlalchemy-and-alembic/ – Ernest Mar 09 '21 at 08:53
6

For alembic 1.5.5 and above, add the following to your alembic.ini:

prepend_sys_path = .

From alembic documentation: this will be prepended to sys.path if present, defaults to the current working directory.

Roman Nakutnyi
  • 791
  • 7
  • 11
  • 3
    This is the best solution. My `alembic` folder is in the project root and by changing this path to `..` I can import models in `app/models.py`. – DSteman Aug 30 '22 at 21:00
  • Great tip! 1.5.5 was released February 2021, so it's not fairly new. See https://github.com/sqlalchemy/alembic/releases/tag/rel_1_5_5 . – Josh Jan 17 '23 at 09:19
  • 1
    this is the correct solution, i was referencing this SO post in a older repo from 2020 and then resetting up alembic in a new repo, i referenced this again, and was pleasantly surprised they added a built in feature to do this. leaving `prepend_sys_path` as the default for me worked , if alembic.ini is in the root folder of your project, alongside the folder that is your 'module' (so `/alembic.ini` and `/your_module_here/some_code.py` etc – mgrandi Apr 30 '23 at 08:27