From 60b6ef3bfe8daabed30d4a5c2900480d0aa550d8 Mon Sep 17 00:00:00 2001 From: fullstack Date: Sun, 18 Oct 2026 06:02:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(alert-rules):=20dynamic-thresholds=20?= =?UTF-8?q?=E8=BF=AD=E4=BB=A3=20Column=20=E5=BD=93=E8=A1=8C=E5=8F=96?= =?UTF-8?q?=E5=80=BC=E8=87=B4=20500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET /api/cma/alert-rules/dynamic-thresholds 在 {c.name: getattr(c, c.name) for c in DynamicThresholdCache.__table__.columns} 里把 Column 对象当 ORM 行取属性,触发 AttributeError: 'Comparator' object has no attribute 'id'。改为列名取自表元数据、 值取自 query 结果行;entity_id 过滤与 limit 50/kpi_id 分支不变。 (cherry picked from commit 4130726be411a74a3318d9dc6bbc518a6c60d0ff) --- backend/app/api/alert_rules.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/app/api/alert_rules.py b/backend/app/api/alert_rules.py index a17ca2bc..419a792e 100644 --- a/backend/app/api/alert_rules.py +++ b/backend/app/api/alert_rules.py @@ -420,8 +420,10 @@ def get_dynamic_thresholds(kpi_id: Optional[int] = None, db: Session = Depends(g query = db.query(DynamicThresholdCache).filter(DynamicThresholdCache.entity_id == entity_id) if kpi_id: query = query.filter(DynamicThresholdCache.kpi_id == kpi_id) - cache = query.order_by(DynamicThresholdCache.id.desc()).limit(50).all() - return {"data": [{c.name: getattr(c, c.name) for c in DynamicThresholdCache.__table__.columns} for c in cache]} + rows = query.order_by(DynamicThresholdCache.id.desc()).limit(50).all() + # 列名取自表元数据,值取自 ORM 行(原实现把 Column 对象当行取值,AttributeError: 'Comparator' object has no attribute 'id') + columns = [col.name for col in DynamicThresholdCache.__table__.columns] + return {"data": [{name: getattr(row, name) for name in columns} for row in rows]} @router.post("/calculate-dynamic")