Issue
I am following the example from sqlalchemy docs https://docs.sqlalchemy.org/en/20/orm/inheritance.html#relationships-with-joined-inheritance
and I am facing this kind of chicken-egg-issue with Mapped[List[Manager]] before actuall Manager class definition what leads to:
sqlalchemy.exc.ArgumentError: Could not interpret annotation Mapped[List[Manager]]. Check that it uses names that are correctly imported at the module level. See chained stack trace for more hints.
what is the proper way to define and then import and create the tables with this new style with Mapped annotate?
all the details:
I store them in models.py
class Company(Base):
__tablename__ = "company"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
managers: Mapped[List[Manager]] = relationship(back_populates="company")
class Employee(Base):
__tablename__ = "employee"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
type: Mapped[str]
__mapper_args__ = {
"polymorphic_identity": "employee",
"polymorphic_on": "type",
}
class Manager(Employee):
__tablename__ = "manager"
id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
manager_name: Mapped[str]
company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
company: Mapped[Company] = relationship(back_populates="managers")
__mapper_args__ = {
"polymorphic_identity": "manager",
}
class Engineer(Employee):
...
inside database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker, declarative_base
engine = create_engine('postgresql+psycopg2://datalake:password@localhost/flask_db')
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
Base.__table_args__ = (
{'schema': 'metadata'}
)
def init_db():
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
import models
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
and inside app.py
from flask import Flask
def create_app():
app = Flask(__name__)
from database import init_db
init_db()
return app
app = create_app()
@app.route('/')
def hello():
return '<h1>Hello, World!</h1>'
when running flask run I get:
sqlalchemy.exc.ArgumentError: Could not interpret annotation Mapped[List[Manager]]. Check that it uses names that are correctly imported at the module level. See chained stack trace for more hints.
the full stack trace
Traceback (most recent call last):
File "/Users/andi/.pyenv/versions/spike/bin/flask", line 8, in <module>
sys.exit(main())
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/flask/cli.py", line 1064, in main
cli.main()
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/core.py", line 1078, in main
rv = self.invoke(ctx)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/core.py", line 1688, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/core.py", line 1434, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/core.py", line 783, in invoke
return __callback(*args, **kwargs)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/decorators.py", line 92, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/click/core.py", line 783, in invoke
return __callback(*args, **kwargs)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/flask/cli.py", line 912, in run_command
raise e from None
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/flask/cli.py", line 898, in run_command
app = info.load_app()
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/flask/cli.py", line 309, in load_app
app = locate_app(import_name, name)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/flask/cli.py", line 219, in locate_app
__import__(module_name)
File "/Users/andi/Projects/sqlalchemy_spike/app.py", line 11, in <module>
app = create_app()
File "/Users/andi/Projects/sqlalchemy_spike/app.py", line 7, in create_app
init_db()
File "/Users/andi/Projects/sqlalchemy_spike/database.py", line 22, in init_db
import models
File "/Users/andi/Projects/sqlalchemy_spike/models.py", line 14, in <module>
class Company(Base):
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_api.py", line 195, in __init__
_as_declarative(reg, cls, dict_)
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py", line 247, in _as_declarative
return _MapperConfig.setup_mapping(registry, cls, dict_, None, {})
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py", line 328, in setup_mapping
return _ClassScanMapperConfig(
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py", line 563, in __init__
self._scan_attributes()
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py", line 1006, in _scan_attributes
collected_annotation = self._collect_annotation(
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py", line 1277, in _collect_annotation
extracted = _extract_mapped_subtype(
File "/Users/andi/.pyenv/versions/3.9.6/envs/spike/lib/python3.9/site-packages/sqlalchemy/orm/util.py", line 2353, in _extract_mapped_subtype
raise sa_exc.ArgumentError(
sqlalchemy.exc.ArgumentError: Could not interpret annotation Mapped[List[Manager]]. Check that it uses names that are correctly imported at the module level. See chained stack trace for more hints.
using:
Flask==3.0.0
SQLAlchemy==2.0.23
Solution
In this situation - where you need to reference a type before it is declared - you can make a forward declaration by passing the type as a string (note that you also need to import List
from the typing
module to complete the type hint.
from typing import List
...
class Company(Base):
...
managers: Mapped[List["Manager"]] = relationship(back_populates="company")
Answered By - snakecharmerb
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.