![](/img/trans.png)
[英]TypeError: issubclass() arg 1 must be a class in Django tests
[英]FastAPI - "TypeError: issubclass() arg 1 must be a class" with modular imports
使用 FastAPI 和 SQLModel 进行模块化导入时,如果我打开 /docs,我会收到以下错误:
TypeError: issubclass() arg 1 必须是 class
这是一个可重现的例子。
用户.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.