Fel*_*lix 9 python sqlalchemy pydantic fastapi sqlmodel
当使用 FastAPI 和 SQLModel 进行模块化导入时,如果打开 /docs,我会收到以下错误:
类型错误:issubclass() arg 1 必须是一个类
这是一个可重现的示例。
用户.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"] = []
Run Code Online (Sandbox Code Playgroud)
项目.py
from sqlmodel import SQLModel, Field
class Item(SQLModel):
id: int = Field(default=None, primary_key=True)
price: float
name: str
Run Code Online (Sandbox Code Playgroud)
主要.py
from fastapi import FastAPI
from user import User
app = FastAPI()
@app.get("/", response_model=User)
def main():
return {"message": "working just fine"}
Run Code Online (Sandbox Code Playgroud)
我按照 sqlmodel https://sqlmodel.tiangolo.com/tutorial/code-struction/#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
Run Code Online (Sandbox Code Playgroud)
Dan*_*erg 11
您需要User.update_forward_refs(Item=Item)
在 OpenAPI 设置之前调用。
所以,这实际上有点棘手,我还不太确定为什么文档中没有提到这一点。也许我错过了一些东西。反正...
如果您跟踪回溯,您将看到发生错误,因为在函数的第 921 行中pydantic.schema
执行field_singleton_schema
了检查以查看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)
Run Code Online (Sandbox Code Playgroud)
但该调用需要在尝试设置该架构之前进行。因此,您至少需要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"}
Run Code Online (Sandbox Code Playgroud)
此时,拥有一个仅包含模型模块的子包并将所有模块导入__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
,但这种方式绝对应该有效。