perf+unify: matches 列表查询优化 + Standings 统一数据源
matches 列表:selectinload 加 load_only 限定列(League.code/Team.name,name_zh/ MatchStats.home_xg,away_xg),补 stats 加载消除 N+1(m.stats 此前懒加载)。 详情接口保持完整 options;游标分页/响应字段/空值语义不变。 Standings 改用 useLeagues() 统一数据源(API 优先,失败回退本地常量), LEAGUES 常量扩展 CL/EL;无数据联赛显示虚线 tab + 空态(非隐藏)。
This commit is contained in:
@@ -5,11 +5,11 @@ from datetime import datetime, timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.orm import selectinload
|
||||
from sqlalchemy.orm import load_only, selectinload
|
||||
|
||||
from src.api.schemas import MatchListOut, MatchOut, PredictionOut
|
||||
from src.db.base import AsyncSession, get_db_read
|
||||
from src.db.models import League, Match, Prediction, Standing
|
||||
from src.db.models import League, Match, MatchStats, Prediction, Standing, Team
|
||||
|
||||
router = APIRouter(prefix="/api/v1", tags=["data"])
|
||||
|
||||
@@ -49,8 +49,20 @@ async def list_matches(
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db_read),
|
||||
):
|
||||
"""比赛列表(游标分页)。"""
|
||||
q = select(Match).options(selectinload(Match.league), selectinload(Match.home_team), selectinload(Match.away_team))
|
||||
"""比赛列表(游标分页)。
|
||||
|
||||
加载策略(列表 vs 详情):
|
||||
- 列表:仅 selectinload 序列化需要的 3 个关系 + stats,且用 load_only 限定列
|
||||
(League.code / Team.name,name_zh / MatchStats.home_xg,away_xg),避免传输全列;
|
||||
同时一次性加载 stats 消除 N+1(m.stats.home_xg 此前触发懒加载)。
|
||||
- 详情(/matches/{id}):保持完整 options(league/teams/stats 全列 + 最近预测)。
|
||||
"""
|
||||
q = select(Match).options(
|
||||
selectinload(Match.league).load_only(League.code),
|
||||
selectinload(Match.home_team).load_only(Team.name, Team.name_zh),
|
||||
selectinload(Match.away_team).load_only(Team.name, Team.name_zh),
|
||||
selectinload(Match.stats).load_only(MatchStats.home_xg, MatchStats.away_xg),
|
||||
)
|
||||
|
||||
if cursor:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user