繁体   English   中英

FastAPI - “TypeError: issubclass() arg 1 must be a class” 带有模块化导入

[英]FastAPI - "TypeError: issubclass() arg 1 must be a class" with modular imports

使用 FastAPI 和 SQLModel 进行模块化导入时,如果我打开 /docs,我会收到以下错误:

TypeError: issubclass() arg 1 必须是 class

  • Python 3.10.6
  • 狂妄自大的 1.10.2
  • fastapi 0.85.2
  • sql模型0.0.8
  • macOS 12.6

这是一个可重现的例子。

用户.py

from typing import List, TYPE_CHECKING, Optional
from sqlmodel import SQLModel, Field

if TYPE_CHECKING:
    from item import Item

class User(SQLModel):
    id: int = Field(default=None, primary_key=True)
    age: Optional[int]
    bought_items: List["Item"] = []

项目.py

from sqlmodel import SQLModel, Field

class Item(SQLModel):
    id: int = Field(default=None, primary_key=True)
    price: float
    name: str

主程序

from fastapi import FastAPI

from user import User

app = FastAPI()

@app.get("/", response_model=User)
def main():
    return {"message": "working just fine"}

我遵循了 sqlmodel https://sqlmodel.tiangolo.com/tutorial/code-structure/#make-circular-imports-work中的教程。 如果我将模型放在同一个文件中,一切正常。 由于我的实际模型非常复杂,因此我需要依赖模块化导入。

追溯:

Traceback (most recent call last):
  File "/Users/felix/opt/anaconda3/envs/fastapi_test/lib/python3.10/site-packages/fastapi/utils.py", line 45, in get_model_definitions
    m_schema, m_definitions, m_nested_models = model_process_schema(
  File "pydantic/schema.py", line 580, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 621, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 254, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 461, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 847, in pydantic.schema.field_singleton_schema
  File "pydantic/schema.py", line 698, in pydantic.schema.field_singleton_sub_fields_schema
  File "pydantic/schema.py", line 526, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 921, in pydantic.schema.field_singleton_schema
  File "/Users/felix/opt/anaconda3/envs/fastapi_test/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class

长话短说

您需要在 OpenAPI 设置之前调用User.update_forward_refs(Item=Item)


解释

所以,这实际上有点棘手,我还不太确定,为什么文档中没有提到这一点。 也许我错过了什么。 反正...

如果您按照回溯,您会看到错误发生是因为在pydantic.schema的第 921 行中的field_singleton_schema function 执行检查以查看issubclass(field_type, BaseModel)并且此时field_type实际上不是type实例。

一些调试表明,当生成User model 的模式并处理bought_items字段时,会发生这种情况。 那时注释被处理并且List的类型参数仍然是对Item前向引用 这意味着它不是实际的Item class 本身。 这就是传递给issubclass并导致错误的原因。

在处理 Pydantic 模型之间的递归或循环关系时,这是一个相当普遍的问题,这就是为什么他们非常友好地为此提供了一种特殊的方法。 它在文档的推迟注释部分进行了解释。 该方法是update_forward_refs ,顾名思义,它用于解析前向引用。

在这种情况下棘手的是,您需要为其提供更新的命名空间以解析Item引用。 为此,您实际上需要在 scope 中拥有真正的Item class,因为这是该命名空间中需要的内容。 你在哪里做并不重要。 例如,您可以将User model 导入您的item模块并在那里调用它(显然在Item的定义下方):

from sqlmodel import SQLModel, Field

from .user import User

class Item(SQLModel):
    id: int = Field(default=None, primary_key=True)
    price: float
    name: str

User.update_forward_refs(Item=Item)

但是该调用需要在尝试设置该模式之前发生。 因此,您至少需要在main模块中导入item模块:

from fastapi import FastAPI

from .user import User
from . import item

api = FastAPI()

@api.get("/", response_model=User)
def main():
    return {"message": "working just fine"}

到那时,拥有一个仅包含 model 模块的子包并将所有这些模块导入该子包的__init__.py中可能会更简单。

我给出将User.update_forward_refs调用放在Item定义下方的示例的原因是,当您实际具有循环关系时,这些情况通常会发生,即如果您的Item class 有一个users字段,例如,它被键入为list[User] 然后你无论如何都必须在那里导入User并且可能只更新那里的引用。

您的具体示例中,您实际上没有任何循环依赖关系,因此严格来说不需要TYPE_CHECKING转义。 你可以简单地做from.item import Item inside user.py并将实际的 class 作为bought_items: list[Item]放在你的注释中。 但我假设您简化了实际用例,只是忘记了包含循环依赖。


也许我遗漏了一些东西,这里的其他人可以找到一种方法来调用update_forward_refs无需显式提供Item ,但这种方式应该绝对有效。

对于最终来到这里的任何人(就像我一样)遇到同样的错误但无法使用上面的解决方案解决它,我的脚本看起来像这样。 似乎SQLModel依赖于pydantic.BaseModel所以这个解决方案也适用于此。

from pydantic import BaseModel

class Model(BaseModel):
    values: list[int, ...]

class SubModel(Model):
    values = list[int, int, int]

我花了很长时间才意识到我的错误是什么,但在SubModel中我使用了= (赋值),而我应该使用: (类型提示)。

最奇怪的是,它确实在 docker 容器 (Linux) 中工作,但在本地 (Windows) 中不工作。 另外,mypy 没有注意到这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM