How to get alembic to recognise models from multiple model files in Flask
Question:
So in Flask I have models.py
which contains all of my model definitions. I’d like to separate this out into multiple model files under a models
directory.
I’ve given this a try by adding some model files such as models/user_model.py
, models/booking_model.py
etc. but alembic doesn’t seem to detect the models in these files.
In the standard alembic.ini with Flask I have:
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Do I need to add something here to get alembic to recognise the models?
Answers:
You need to edit your env.py:
# add your model's MetaData object here
# for 'autogenerate' support
from your_cool_app.models import *
target_metadata = db.Model.metadata
To complete this answer, you can directly put all your ‘Bases’ in a list like so :
from myapp.mymodel1 import Model1Base
from myapp.mymodel2 import Model2Base
target_metadata = [Model1Base.metadata, Model2Base.metadata]
Link to the doc : http://alembic.zzzcomputing.com/en/latest/autogenerate.html#autogenerating-multiple-metadata-collections
I ran into the exact same thing.
Here’s my structure
.. alembic
.. .. alembic stuff
.. database
.. .. __init__.py
.. .. models.py
.. .. my_table_one.py
.. .. my_table_two.py
create base in your init as such:
# creates Base
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
Then in my models.py I import Base and all my tables
from database import Base
from database.my_table_one import MyTableOne
from database.my_table_two import MyTableTwo
in my tables I import base from the database module
from database import Base
and finally my env.py is very simply this:
from database.models import Base
target_metadata = [Base.metadata]
the reason for this is so that I don’t have to maintain env.py every time I add a table. I add it to the models.py in my database module and that’s it.
If you declare a ModelBase
class and import them into each of your model files, you can then put the location of each model file into a tuple inside the env.py
file, and then use importlib
to import them. This will make adding new model files as easy as updating the tuple. Here’s an example:
in project/models/__init__.py
from sqlalchemy import DeclarativeBase
class ModelBase(DeclarativeBase): ...
in project/models/modelfile1.py
from project.models import ModelBase
class YourModel(ModelBase):
# ...
and in alembic/env.py
import importlib
# ...
from project.models import ModelBase
WANT_MODEL_FILES = (
'project.models.modelfile1',
'project.models.modelfile2',
)
for want_model_file in WANT_MODEL_FILES:
try:
loaded_module = importlib.import_module(want_model_file )
except ModuleNotFoundError:
print(f'Could not import module {want_module}')
target_metadata = ModelBase.metadata
By wrapping the importing business with a try/except, deleting a model file won’t cause the whole thing to crater, and alembic will determine that you want to delete the related tables.
So in Flask I have models.py
which contains all of my model definitions. I’d like to separate this out into multiple model files under a models
directory.
I’ve given this a try by adding some model files such as models/user_model.py
, models/booking_model.py
etc. but alembic doesn’t seem to detect the models in these files.
In the standard alembic.ini with Flask I have:
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Do I need to add something here to get alembic to recognise the models?
You need to edit your env.py:
# add your model's MetaData object here
# for 'autogenerate' support
from your_cool_app.models import *
target_metadata = db.Model.metadata
To complete this answer, you can directly put all your ‘Bases’ in a list like so :
from myapp.mymodel1 import Model1Base
from myapp.mymodel2 import Model2Base
target_metadata = [Model1Base.metadata, Model2Base.metadata]
Link to the doc : http://alembic.zzzcomputing.com/en/latest/autogenerate.html#autogenerating-multiple-metadata-collections
I ran into the exact same thing.
Here’s my structure
.. alembic
.. .. alembic stuff
.. database
.. .. __init__.py
.. .. models.py
.. .. my_table_one.py
.. .. my_table_two.py
create base in your init as such:
# creates Base
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
Then in my models.py I import Base and all my tables
from database import Base
from database.my_table_one import MyTableOne
from database.my_table_two import MyTableTwo
in my tables I import base from the database module
from database import Base
and finally my env.py is very simply this:
from database.models import Base
target_metadata = [Base.metadata]
the reason for this is so that I don’t have to maintain env.py every time I add a table. I add it to the models.py in my database module and that’s it.
If you declare a ModelBase
class and import them into each of your model files, you can then put the location of each model file into a tuple inside the env.py
file, and then use importlib
to import them. This will make adding new model files as easy as updating the tuple. Here’s an example:
in project/models/__init__.py
from sqlalchemy import DeclarativeBase
class ModelBase(DeclarativeBase): ...
in project/models/modelfile1.py
from project.models import ModelBase
class YourModel(ModelBase):
# ...
and in alembic/env.py
import importlib
# ...
from project.models import ModelBase
WANT_MODEL_FILES = (
'project.models.modelfile1',
'project.models.modelfile2',
)
for want_model_file in WANT_MODEL_FILES:
try:
loaded_module = importlib.import_module(want_model_file )
except ModuleNotFoundError:
print(f'Could not import module {want_module}')
target_metadata = ModelBase.metadata
By wrapping the importing business with a try/except, deleting a model file won’t cause the whole thing to crater, and alembic will determine that you want to delete the related tables.