在使用FastAPI和SQLModel进行模块化导入时,如果打开/docs,将得到以下错误:
TypeError: issubclass() arg 1必须是一个类
这里是一个可重复的例子。
user.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"] = []
item.py
from sqlmodel import SQLModel, Field
class Item(SQLModel):
id: int = Field(default=None, primary_key=True)
price: float
name: str
main.py
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
发布于 2022-11-07 21:02:02
TL;DR
您需要在User.update_forward_refs(Item=Item)
设置之前调用OpenAPI。
解释
因此,这实际上要复杂得多,我还不太确定,为什么在文档中没有提到这一点。也许我遗漏了什么。不管怎样..。
如果您遵循跟踪,您将看到错误发生,因为在pydantic.schema
的field_singleton_schema
函数的第921行中,执行了一次检查,以查看issubclass(field_type, BaseModel)
和在这一点上field_type
是否实际上不是type
实例。
调试表明,当生成User
模型的模式并处理bought_items
字段时,就会发生这种情况。此时,注释将被处理,List
的类型参数仍然是到Item
的正向参考。这意味着它不是实际的Item
类本身。这就是传递给issubclass
并导致错误的原因。
这是一个相当常见的问题,在处理Pydantic模型之间的递归或循环关系时,这就是为什么它们如此友好地提供了一种特殊的方法。它将在文档的延迟注解部分中解释。这个方法是update_forward_refs
,顾名思义,它是用来解析前向引用的。
在这种情况下,需要使用更新的命名空间来解析Item
引用,这是很棘手的。要做到这一点,您需要,实际上,在作用域中有真正的Item
类,因为这是该名称空间中需要的。你在哪里做这件事并不重要。例如,您可以将User
模型导入到您的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)
但是,在尝试设置该架构之前,需要进行该调用。因此,您至少需要在item
模块中导入main
模块:
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"}
在这一点上,拥有一个只有模型模块的子包并将它们全部导入到该子包的__init__.py
中可能更简单。
我给出了将User.update_forward_refs
调用放在Item
定义下面的示例,原因是当您实际上有一个循环关系时,通常会发生这些情况,例如,如果您的Item
类有一个users
字段,该字段被键入为list[User]
。那么,无论如何,您必须在那里导入User
,最好只是更新那里的引用。
在您的特定示例中,实际上没有任何循环依赖项,因此严格地说,不需要TYPE_CHECKING
转义。您只需在from .item import Item
中执行user.py
,并将实际的类作为bought_items: list[Item]
放在注释中。但是,我假设您简化了实际用例,并且简单地忘记了包括循环依赖。
也许我遗漏了一些东西,这里的其他人可以找到一种方法来调用update_forward_refs
,而不需要显式地提供Item
,但是这种方式肯定会起作用。
https://stackoverflow.com/questions/74346565
复制相似问题