单独文件中的 FastAPI / Pydantic 循环引用
Posted
技术标签:
【中文标题】单独文件中的 FastAPI / Pydantic 循环引用【英文标题】:FastAPI / Pydantic circular references in separate files 【发布时间】:2020-12-04 19:42:22 【问题描述】:我希望在 FastAPI 中使用类似于以下内容的架构:
from __future__ import annotations
from typing import List
from pydantic import BaseModel
class Project(BaseModel):
members: List[User]
class User(BaseModel):
projects: List[Project]
Project.update_forward_refs()
但为了保持我的项目结构干净,我会。喜欢在单独的文件中定义这些。如果不创建循环引用,我怎么能做到这一点?
使用上面的代码,FastAPI 中的模式生成工作正常,我只是不知道如何将它分成单独的文件。在稍后的步骤中,我将不使用属性,而是使用 @property
s 在它们的子类中定义这些对象的 getter。但是对于 OpenAPI 文档生成,我需要将其结合起来 - 我认为。
【问题讨论】:
请帮我理解清楚,因为我知道您想将class User
存储在models_user.py
中,而您想将class Project
存储在models_project.py
中,对吗?
是的,计划是这样的
【参考方案1】:
循环依赖可能在 Python 中起作用的三种情况:
模块顶部:import package.module
模块底部:from package.module import attribute
最重要的功能:两者都适用
在您的情况下,第二种情况“模块底部”会有所帮助。
因为你 need to use update_forward_refs
函数来解决 pydantic 的延迟注释,如下所示:
# project.py
from typing import List
from pydantic import BaseModel
class Project(BaseModel):
members: "List[User]"
from user import User
Project.update_forward_refs()
# user.py
from typing import List
from pydantic import BaseModel
class User(BaseModel):
projects: "List[Project]"
from project import Project
User.update_forward_refs()
不过,我强烈建议您不要有意引入循环依赖项
【讨论】:
嗨,亚历克斯,感谢您的回答和输入 - 我同意我需要找到另一种方法来进行建模!【参考方案2】:如果我想将模型/模式拆分为单独的文件,我将为 ProjectBase 模型和 UserBase 模型创建额外的文件,以便 Project 模型和 User 模型可以从它们继承。我会这样做:
#project_base.py
from pydantic import BaseModel
class ProjectBase(BaseModel):
id: int
title: str
class Config:
orm_mode=True
#user_base.py
from pydantic import BaseModel
class UserBase(BaseModel):
id: int
title: str
class Config:
orm_mode=True
#project.py
from typing import List
from .project_base import ProjectBase
from .user_base import UserBase
class Project(ProjectBase):
members: List[UserBase] = []
#user.py
from typing import List
from .project_base import ProjectBase
from .user_base import UserBase
class User(UserBase):
projects: List[ProjectBase] = []
注意:对于这个方法,orm_mode 必须放在 ProjectBase 和 UserBase 中,这样即使不是 dict 也可以被 Project 和 User 读取
【讨论】:
【参考方案3】:只需将所有架构imports
放在文件底部,在所有类之后,然后调用update_forward_refs()
。
#1/4
from __future__ import annotations # this is important to have at the top
from pydantic import BaseModel
#2/4
class A(BaseModel):
my_x: X # a pydantic schema from another file
class B(BaseModel):
my_y: Y # a pydantic schema from another file
class C(BaseModel):
my_z: int
#3/4
from myapp.schemas.x import X # related schemas we import after all classes
from myapp.schemas.y import Y
#4/4
A.update_forward_refs() # tell the system that A has a related pydantic schema
B.update_forward_refs() # tell the system that B has a related pydantic schema
# for C we don't need it, because C has just an integer field.
注意: 在每个具有架构导入的文件中执行此操作。 这将使您能够进行任何组合而不会出现循环导入问题。
注意 2:
人们通常将导入和update_forward_refs()
放在每个class
之后,然后报告它不起作用。这通常是因为如果应用程序很复杂,您不知道 import
正在调用哪个 class
以及何时调用。因此,如果将其放在底部,则可以确定每个 class
都会被“扫描”并对其他人可见。
【讨论】:
以上是关于单独文件中的 FastAPI / Pydantic 循环引用的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 pydantic/fastapi 接受具有数组\单个项目的单个路由对象?