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

FastAPI+SQLAlchemy联表查询时vars()序列化错误求助

问题原因与解决方法

错误原因

你的查询返回的是元组列表,每个元组包含SQLAlchemy的Product模型实例和votes数值。FastAPI的jsonable_encoder无法直接序列化这种混合结构:元组本身没有__dict__属性,默认序列化逻辑无法正确处理元组内模型实例与数值的组合,因此抛出vars()相关错误。

解决方法

方法一:手动转换为可序列化字典

遍历查询结果,将每个元组转换成符合期望格式的字典,同时清理SQLAlchemy模型的内部属性:

@router.get("/")
def get_products(db: Session = Depends(get_db)):
    products = db.query(models.Product, func.count(models.Vote.product_id).label("votes")).join(
        models.Vote, models.Vote.product_id == models.Product.id, isouter=True).group_by(models.Product.id).all()
    
    result = []
    for product, votes in products:
        # 提取Product字段,排除SQLAlchemy内部属性
        product_dict = {k: v for k, v in product.__dict__.items() if not k.startswith('_')}
        result.append({
            "Product": product_dict,
            "votes": votes
        })
    return result

方法二:使用SQLAlchemy inspect工具转换模型

利用SQLAlchemy的inspect方法获取模型的持久化属性,避免手动过滤内部字段:

from sqlalchemy import inspect

@router.get("/")
def get_products(db: Session = Depends(get_db)):
    products = db.query(models.Product, func.count(models.Vote.product_id).label("votes")).join(
        models.Vote, models.Vote.product_id == models.Product.id, isouter=True).group_by(models.Product.id).all()
    
    result = []
    for product, votes in products:
        # 获取模型的属性字典并提取值
        product_attrs = inspect(product).attrs
        clean_product = {k: v.value for k, v in product_attrs.items()}
        result.append({
            "Product": clean_product,
            "votes": votes
        })
    return result

方法三:使用Pydantic模型定义返回结构(推荐)

创建匹配期望输出的Pydantic模型,利用其ORM序列化能力自动处理转换:

首先定义Pydantic模型:

from pydantic import BaseModel
from datetime import datetime

class ProductResponse(BaseModel):
    id: int
    is_available: bool
    user_id: int
    title: str
    price: float
    created_at: datetime

    class Config:
        orm_mode = True  # 支持从SQLAlchemy模型实例初始化

class ProductWithVotes(BaseModel):
    Product: ProductResponse
    votes: int

然后修改接口:

@router.get("/", response_model=list[ProductWithVotes])
def get_products(db: Session = Depends(get_db)):
    products = db.query(models.Product, func.count(models.Vote.product_id).label("votes")).join(
        models.Vote, models.Vote.product_id == models.Product.id, isouter=True).group_by(models.Product.id).all()
    
    return [ProductWithVotes(Product=product, votes=votes) for product, votes in products]

这种方法符合FastAPI最佳实践,自动生成接口文档,同时保证序列化的正确性。


内容的提问来源于stack exchange,提问作者Nishchit Bhandari

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:53:10