我无法弄清楚如何使用fastapi和sqlmodel显示一对多关系。我已经通读了这个问题,但我的情况似乎略有不同。特别是在函数调用中。
这是我的schemas.py
:
from typing import Optional
from sqlmodel import Field, Relationship, SQLModel
class BinaryBase(SQLModel):
product_id: int
software_install_path: Optional[str] = None
host_id: int = Field(foreign_key="host.id", nullable=False)
class Binary(BinaryBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
host_id: int = Field(foreign_key="host.id", nullable=False)
host: "Host" = Relationship(back_populates="binaries")
class HostBase(SQLModel):
name: str
region: Optional[str] = None
os_version: Optional[str] = None
network: Optional[int] = None
class Host(HostBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
binaries: list[Binary] = Relationship(back_populates='host')
class HostReadWithBinary(HostBase):
bins: list[HostBase] = []
class BinaryReadWithHost(BinaryBase):
host: BinaryBase
这是我的main.py
:
from fastapi import Depends, FastAPI
from sqlmodel import Session, col, select
...
@app.get(
"/binaries/",
response_model=list[HostReadWithBinary]
)
def get_binary(name: Optional[str] = None, session: Session = Depends(get_session)) -> list[HostReadWithBinary]:
query = select(Host).limit(100)
if name:
query = query.where(col(Host.name).contains(name.lower()))
return session.exec(query).all()
Host
表表示1
部分,Binary
表表示many
部分。我想热切地得到所有主机的所有BinaryBase
属性的回应。但我得到的是:
[
{
"name": "hkl20014889",
"region": "HK",
"os_version": "Red Hat 6.10",
"network": 3,
"bins": []
},
{
"name": "hkl20016283",
"region": "HK",
"os_version": "Red Hat 6.10",
"network": 3,
"bins": []
},
....
从理论上讲,当id
在二进制中Host
联接host_id
时,bins
应该保存Host
表的属性。
您需要意识到,当您为路由定义response_model
时,它将始终尝试通过该模型解析来自路由处理程序函数(在本例中为get_binary
)的任何数据。这是通过在响应模型上调用.from_orm
方法来完成的,该方法遍历在其上定义的所有字段,并尝试在您传递给它的对象上查找相应的属性(即具有相同名称)。
您指定的模型(在列表中,但参数成立)HostReadWithBinary
。除了在其父模型上定义的字段HostBase
它只有字段bins
,这应该是HostBase
的列表。
首先,我认为您的意思是声明bins
字段属于list[BinaryBase]
类型,而不是list[HostBase]
。如果您正确命名了该字段,这将导致错误,但这是您的第二个错误出现的地方。
您还将响应模型上的字段命名为bins
,但您的处理程序函数执行返回Host
模型实例列表的查询。该模型没有bins
字段。它有一个名为binaries
的字段。这意味着当from_orm
方法到达HostReadWithBinary.bins
字段时,它会检查相应的Host
实例是否具有名为bins
的属性。它找不到,但没问题,因为您为HostReadWithBinary.bins
设置了默认值,即空列表[]
,因此这是在每个生成的响应模型实例上设置的内容。
因此,您应该能够通过更改响应模型定义来修复错误,如下所示:
class HostReadWithBinary(HostBase):
binaries: list[BinaryBase] = []
或者,您可以更改Host
模型上关系字段的名称:
class Host(HostBase, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
bins: list[Binary] = Relationship(back_populates='host')
class HostReadWithBinary(HostBase):
bins: list[BinaryBase] = []
它们需要相同,否则将一个模型的对象解析为另一个模型将无法工作(正确)。
旁注:您还错误地用BinaryBase
而不是HostBase
注释了BinaryReadWithHost
上的host
字段。
PS:我也刚刚注意到一个与类型注释相关的小错误。你声明路由处理程序函数的返回类型是list[HostReadWithBinary]
,但这不是它返回的内容。它返回list[Host]
.这是对响应模型误解的一部分。路线的装饰版本是返回list[HostReadWithBinary]
.路由处理程序本身get_binary
(即在装饰之前)返回list[Host]
,然后将其传递给它周围的包装器,该包装器将其解析为list[HostReadWithBinary]
并在途中发送该数据(最终发送给客户端)。这个包装器操作显然发生在幕后,是 FastAPI 装饰器魔术的一部分。