0

我有一些具有多态基类定义和两个子类的遗留代码。

实体定义如下。

class Field(Base):
    __tablename__ = "field"
    field_parent = Column(String(64), index=True)

    controller_id = Column(ForeignKey('controller.id'))
    controller = relationship("Controller")
    node_id = Column(ForeignKey("node.id"))
    __mapper_args__ = {"polymorphic_on": field_parent}

class ControllerField(Field):
    __mapper_args__ = {"polymorphic_identity": "controller"}

class NodeField(Field):
    __mapper_args__ = {"polymorphic_identity": "node"}

class Controller:
    __tablename__ = "controller"

class Node:
    __tablename__ = "node"

来自我本地数据库的一些示例数据。

在此处输入图像描述

我正在尝试查询“控制器”表,而 SQLAlchemy 正在尝试加载所有类的关系(我的假设)。当它到达多态子类时,查询失败,因为“字段”表包含对“控制器”和“节点”表的外键引用,并且因为后者应该是“节点”类型,所以它仍然有一个 FK “控制器”表,因此身份不是正确的类型。在当前配置中,SQLAlchemy 似乎不喜欢这样。可以在下面看到堆栈跟踪。

    File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 78, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 580, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 390, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 78, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 580, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 241, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 52, in app
    response = await func(request)
  File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 201, in app
    raw_response = await run_endpoint_function(
  File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 150, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "/usr/local/lib/python3.9/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool
    return await loop.run_in_executor(None, func, *args)
  File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/src/app/./app/subapp/shim/v2/endpoints/controller.py", line 25, in get
    return Wrapper(data=service.get(id=id, controller_category=controllerCategory, condensed=condensed))
  File "/usr/src/app/./app/subapp/shim/services/controller_service.py", line 23, in get
    "controller": self.__append_nodes(controller.dict(logged_in_user=self.__logged_in_user))
  File "/usr/src/app/./app/database/base_new.py", line 28, in dict
    rs = getattr(self, relationship.key)
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 481, in __get__
    return self.impl.get(state, dict_)
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 926, in get
    value = self._fire_loader_callables(state, key, passive)
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 962, in _fire_loader_callables
    return self.callable_(state, passive)
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/strategies.py", line 892, in _load_for_state
    return self._emit_lazyload(
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/strategies.py", line 1056, in _emit_lazyload
    result = result.unique().scalars().all()
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1319, in all
    return self._allrows()
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 400, in _allrows
    rows = self._fetchall_impl()
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1232, in _fetchall_impl
    return self._real_result._fetchall_impl()
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1626, in _fetchall_impl
    return list(self.iterator)
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 124, in chunks
    rows = [proc(row) for row in fetch]
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 124, in <listcomp>
    rows = [proc(row) for row in fetch]
  File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 1274, in polymorphic_instance
    raise sa_exc.InvalidRequestError(
sqlalchemy.exc.InvalidRequestError: Row with identity key (<class 'app.database.models.field.Field'>, (150,), None) can't be loaded into an object; the polymorphic discriminator column 'field.field_parent' refers to mapped class NodeField->field, which is not a sub-mapper of the requested mapped class ControllerField->field

如果我删除对具有身份“节点”的实例上的“控制器”表的引用,查询将按预期运行。由于这是遗留代码,我不能只删除额外的 FK 引用,因为我们不知道这样做会破坏什么。有没有一种方法可以告诉 SQLAlchemy 在不查看 FK 参考或其他内容的情况下加载正确的定义?

额外信息:

FastAPI Docker 容器与 MySQL 数据库 Python 3.8 SQLAlchemy 1.4.21

4

0 回答 0