簡體   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