You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLAlchemy+FastAPI:如何实现Owner关联猫狗的合并宠物结构返回?

问题描述

我一直在StackOverflow上查找该问题的解决方案,但始终未能找到(若有相关链接欢迎提供),同时也查阅了SQLAlchemy、FastAPI和Pydantic的官方文档。

我正在使用SQLAlchemy + FastAPI + VueJS技术栈搭建网站,遇到了返回特定JSON结构的问题,具体细节如下:

数据库表结构(model.py)

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()

class Owner(Base):
    __tablename__ = "owner"

    id = Column(Integer, primary_key=True)
    # 其他Owner表字段

    dog_relationship = relationship("Dog", backref="owner")
    cat_relationship = relationship("Cat", backref="owner")

class Dog(Base):
    __tablename__="dog"

    id = Column(Integer, primary_key=True)
    # 与Cat表相同的字段,额外包含专属字段
    ownerID = Column(Integer, ForeignKey('owner.id'), index=True)

class Cat(Base):
    __tablename__="cat"

    id = Column(Integer, primary_key=True)
    # 与Dog表相同的字段,无Dog的专属字段
    ownerID = Column(Integer, ForeignKey('owner.id'), index=True)

注:已建立Owner与Dog、Cat的一对多关联关系。

Pydantic Schema配置(schemas.py)

from pydantic import BaseModel, Field, Annotated
from typing import Union, Optional, Literal, List

class OwnerBase(BaseModel):
    # 匹配Owner表字段

class OwnerReturn(OwnerBase):
    id : int

    class Config:
        from_attributes = True

class DogBase(BaseModel):
    # 匹配Dog表字段
    pet_type: Literal["Dog"]
    ownerID : int

class DogReturn(DogBase):
    id: int
    owner : OwnerReturn

    class Config:
        from_attributes = True

class CatBase(BaseModel):
    # 匹配Cat表字段
    pet_type: Literal["Cat"]
    ownerID : int

class CatReturn(CatBase):
    id: int
    owner : OwnerReturn

    class Config:
        from_attributes = True

# 用于合并Dog和Cat的联合类型
CombinedDogCats = Annotated[Union[DogReturn, CatReturn], Field(discriminator="pet_type")]

# 最初的返回模型,不符合PrimeVue表格需求
class OwnerReturnWithDogsCats(OwnerReturn):
   dog_relationship : list[DogBase]
   cat_relationship : list[CatBase]

# 合并后的宠物模型,Cat的Dog专属字段返回null
class CombinedPets(BaseModel):
    # 包含Dog所有字段,其中Dog专属字段设为Optional[int] = None
    id: int
    # 其他公共字段
    pet_type: Literal["Dog", "Cat"]
    ownerID: int
    # Dog专属字段,Cat返回null
    dog_specific_field1: Optional[int] = None
    dog_specific_field2: Optional[int] = None

接口定义

from fastapi import APIRouter, Depends, Request
from sqlalchemy.orm import Session
from .schemas import CombinedPets
from .models import Owner
from .database import get_db

router = APIRouter()

@router.get('/', response_model=list[CombinedPets])
def get_CombinedPets(request: Request, db:Session = Depends(get_db)):
    # 查询所有Owner记录
    owners = db.query(Owner).all()

    dogs_cats_list: list[CombinedPets] = []

    # 遍历Owner,收集所有宠物
    for owner in owners:
        for dog in owner.dog_relationship:
            dogs_cats_list.append(dog)
        for cat in owner.cat_relationship:
            dogs_cats_list.append(cat)
    
    return dogs_cats_list

当前返回与期望结构

  • 普通查询接口返回:
[
  {
    "owner info",
    "dog_relationship": [{"dog info"}],
    "cat_relationship": [{"cat info"}]
  }
]
  • get_CombinedPets接口返回:
[
  {"dog info"},
  {"cat info"},
  ...
]
  • 期望返回结构:
[
  {
    "owner info",
    "combinedPets": [
      {"pet_type": "Dog", "dog info", "dog专属字段": 值},
      {"pet_type": "Cat", "cat info", "dog专属字段": null},
      ...
    ]
  },
  ...
]

核心疑问

CombinedPets模型的结构符合单条宠物的需求,但Owner表并无名为combinedPets的关联关系。请问是否可通过SQLAlchemy + FastAPI的配置实现该需求?还是需要手动构造包含combinedPets的字典?手动构造时担心无法准确区分宠物所属的Owner。


解决方案

方式一:通过Pydantic模型直接构造(推荐)

不需要修改SQLAlchemy模型,直接在Pydantic中定义包含combinedPets的返回模型,然后手动组合数据:

  1. 定义新的返回模型:
class OwnerWithCombinedPets(OwnerReturn):
    combinedPets: List[CombinedPets]

    class Config:
        from_attributes = True
  1. 修改接口实现:
@router.get('/', response_model=list[OwnerWithCombinedPets])
def get_owners_with_combined_pets(request: Request, db:Session = Depends(get_db)):
    owners = db.query(Owner).all()
    result = []
    for owner in owners:
        # 组合当前Owner的所有宠物
        combined_pets = []
        # 转换Dog为CombinedPets
        for dog in owner.dog_relationship:
            combined_pets.append(CombinedPets.from_orm(dog))
        # 转换Cat为CombinedPets,自动填充Dog专属字段为null
        for cat in owner.cat_relationship:
            combined_pets.append(CombinedPets.from_orm(cat))
        # 构造Owner返回对象
        owner_data = OwnerWithCombinedPets.from_orm(owner)
        owner_data.combinedPets = combined_pets
        result.append(owner_data)
    return result

这种方式完全通过Pydantic的from_orm方法转换ORM对象,确保宠物与所属Owner的关联准确——所有宠物都是从当前Owner的关联关系中直接获取,不会混淆所属关系。

方式二:SQLAlchemy混合属性(hybrid_property)

可以在Owner模型中添加一个混合属性,直接返回合并后的宠物列表:

  1. 修改Owner模型:
from sqlalchemy.ext.hybrid import hybrid_property

class Owner(Base):
    __tablename__ = "owner"

    id = Column(Integer, primary_key=True)
    # 其他Owner表字段

    dog_relationship = relationship("Dog", backref="owner")
    cat_relationship = relationship("Cat", backref="owner")

    @hybrid_property
    def combinedPets(self):
        return self.dog_relationship + self.cat_relationship
  1. 定义对应的Pydantic模型:
class OwnerWithCombinedPets(OwnerReturn):
    combinedPets: List[CombinedPets]

    class Config:
        from_attributes = True
  1. 简化接口实现:
@router.get('/', response_model=list[OwnerWithCombinedPets])
def get_owners_with_combined_pets(request: Request, db:Session = Depends(get_db)):
    owners = db.query(Owner).all()
    return [OwnerWithCombinedPets.from_orm(owner) for owner in owners]

这种方式通过SQLAlchemy的混合属性让Owner模型直接拥有combinedPets属性,Pydantic可以自动识别并转换,代码更简洁。需要注意的是,这个混合属性是在Python层面合并的列表,不会影响数据库查询逻辑。


内容的提问来源于stack exchange,提问作者Yoili Youth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 20:57:10