"""ORM 通用辅助""" from django.db.models import Subquery, OuterRef, Sum, Value, DecimalField from django.db.models.functions import Coalesce def inflight_sum(model, fk, submit_isnull_path, amount, *, max_digits=12, decimal_places=1): """未提交单据的"在途"聚合量(存量之外正在出入库/在制/在途交接的数量)。 用关联标量子查询实现, 不与主表 JOIN, 因此可在同一个 queryset 上安全叠加 多个不同多值关系的聚合而不会因笛卡尔积翻倍(Django 多聚合经典陷阱)。 :param model: 明细模型, 如 MIOItem / Mlogb / Handoverb :param fk: 明细指向主表的外键名, 如 'mb' / 'wm_in' / 'wm' :param submit_isnull_path: 判定"未提交"的查询路径, 如 'mio__submit_time__isnull' :param amount: 参与求和的数量字段, 如 'count' / 'count_use' """ return Coalesce( Subquery( model.objects .filter(**{fk: OuterRef('pk'), submit_isnull_path: True}) .order_by() # 清掉默认排序, 否则污染 GROUP BY .values(fk) # 按外键分组 -> 每个主键一行 .annotate(_s=Sum(amount)) .values('_s') # 取出标量 ), Value(0, output_field=DecimalField(max_digits=max_digits, decimal_places=decimal_places)), ) def restql_wants(request, field_names): """django-restql 稀疏字段判断: 用于"不需要的字段就不做昂贵注解"。 未带 query 参数 -> 视为需要全部字段(保持默认行为); 带了 query -> 仅当其中点名了任一 field_names 时才认为需要。 """ query = request.query_params.get('query') if request else None if not query: return True return any(name in query for name in field_names)