[英]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.