从 SQLMODEL 获取联接表作为 fastAPI 中的嵌套响应模型



我无法弄清楚如何使用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 装饰器魔术的一部分。

最新更新