SQLModel+FastAPI:添加计算字段且隐藏关联purchases字段的方法
解决方法
方法1:查询阶段直接计算聚合值(推荐)
直接在数据库查询时通过SQL聚合函数统计total_spent,不需要在模型中关联完整的purchases列表,既提升查询效率,也不会返回冗余数据。
示例代码:
from sqlmodel import select, func, join # 构造查询语句,关联Purchase表计算总支出 statement = ( select( Budget.id, Budget.name, Budget.monthly_income, # 用coalesce处理无采购记录的情况,默认返回0.0 func.coalesce(func.sum(Purchase.price), 0.0).label("total_spent") ) .outerjoin(Purchase, Budget.id == Purchase.budget_id) .group_by(Budget.id) ) # 执行查询 results = session.exec(statement).all() # 定义带统计字段的公开模型 class BudgetPublicWithStats(BudgetPublic): total_spent: float # 将查询结果转换为响应模型 budget_stats_list = [BudgetPublicWithStats(**result._asdict()) for result in results]
返回的budget_stats_list仅包含预算基础信息和总支出,不会携带purchases字段。
方法2:私有字段+序列化排除
如果要在模型层面用@computed_field计算,可以添加私有_purchases字段,同时通过exclude=True标记该字段不参与序列化,避免返回给客户端。
示例代码:
from sqlmodel import Field, computed_field class BudgetPublic(BudgetBase): id: int # 私有字段,设置exclude=True排除序列化 _purchases: list[Purchase] = Field(..., exclude=True) @computed_field def total_spent(self) -> float: return sum(p.price for p in self._purchases) if self._purchases else 0.0
注意:查询时需要确保_purchases被正确加载(比如用selectinload(Budget._purchases)),但该字段不会出现在最终API响应中,仅保留计算后的total_spent。
方法3:分离模型职责
创建专门的统计模型,与包含purchases的模型解耦。在业务逻辑中手动计算统计值并填充到新模型,完全控制返回字段。
示例代码:
# 定义带多维度统计的公开模型 class BudgetPublicWithStats(BudgetPublic): total_spent: float average_purchase: float | None = None purchase_count: int = 0 # 业务逻辑处理 budget = session.get(Budget, budget_id, options=[selectinload(Budget.purchases)]) purchase_count = len(budget.purchases) if purchase_count > 0: total_spent = sum(p.price for p in budget.purchases) average_purchase = total_spent / purchase_count else: total_spent = 0.0 average_purchase = None # 构造响应模型 response = BudgetPublicWithStats( id=budget.id, name=budget.name, monthly_income=budget.monthly_income, total_spent=total_spent, average_purchase=average_purchase, purchase_count=purchase_count )
这种方式适合需要多个统计维度的场景,完全自定义返回内容。
内容的提问来源于stack exchange,提问作者Gronnmann
相关产品推荐
相关产品推荐

