单独文件中的 FastAPI / Pydantic 循环引用
FastAPI / Pydantic circular references in separate files
我喜欢在 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()
但为了保持我的项目结构整洁,我会选择 ofc。喜欢在单独的文件中定义这些。如果不创建循环引用,我怎么能做到这一点?
使用上面的代码,FastAPI 中的模式生成工作正常,我只是不知道如何将它分离到单独的文件中。在后面的步骤中,我将不使用属性,而是使用 @property
s 来为这些对象的子类中的对象定义 getter。但是对于 OpenAPI 文档生成,我需要这个组合 - 我想。
在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()
尽管如此,我强烈反对你故意引入循环依赖
如果我想将 models/schema 拆分成单独的文件,我将为 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 中,这样即使它不是字典也可以被 Project 和 User 读取
只需将所有架构 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
都是 'scanned' 并且对其他人可见。
我喜欢在 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()
但为了保持我的项目结构整洁,我会选择 ofc。喜欢在单独的文件中定义这些。如果不创建循环引用,我怎么能做到这一点?
使用上面的代码,FastAPI 中的模式生成工作正常,我只是不知道如何将它分离到单独的文件中。在后面的步骤中,我将不使用属性,而是使用 @property
s 来为这些对象的子类中的对象定义 getter。但是对于 OpenAPI 文档生成,我需要这个组合 - 我想。
在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()
尽管如此,我强烈反对你故意引入循环依赖
如果我想将 models/schema 拆分成单独的文件,我将为 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 中,这样即使它不是字典也可以被 Project 和 User 读取
只需将所有架构 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
都是 'scanned' 并且对其他人可见。