Django中基于ManyToManyField关联表join后分组并取最新记录的方法
问题场景
你有两个存在多对多关联的Django模型,需要查询所有Education实例,以及每个实例关联的created_at最新的Price的cost值,每个Education仅返回一条结果。
原有查询问题说明
你当前使用的select_related('Price')是错误用法:select_related仅支持一对一、外键这类单值关联,多对多关联需要用prefetch_related;且原有查询会返回Education和关联Price的笛卡尔积,会出现同一个Education对应多条记录的情况,不符合需求。
实现方案
方案1:子查询实现(数据库层面过滤,性能更优)
直接在数据库层面完成最新Price的匹配,适合数据量较大的场景:
from django.db.models import OuterRef, Subquery # 构造子查询:匹配当前Education关联的Price,按创建时间倒序取第一条的cost值 latest_price_subquery = Price.objects.filter( education=OuterRef('pk') ).order_by('-created_at').values('cost')[:1] # 给每个Education实例注解最新的cost值 result = Education.objects.annotate( latest_cost=Subquery(latest_price_subquery) ).values_list('id', 'title', 'content', 'latest_cost')
如果需要获取完整的Education模型实例而非元组,去掉values_list直接遍历即可,每个实例会新增latest_cost属性存储最新的cost值。
方案2:Prefetch预取实现(内存层面过滤,适合关联数据量小的场景)
先预取所有关联的Price并按创建时间排序,之后在内存中取第一个值:
from django.db.models import Prefetch # 预取关联的Price,按创建时间倒序排序,存入自定义属性sorted_prices result = Education.objects.prefetch_related( Prefetch( 'price', queryset=Price.objects.order_by('-created_at'), to_attr='sorted_prices' ) ).all() # 遍历获取每个Education的最新cost for edu in result: latest_cost = edu.sorted_prices[0].cost if edu.sorted_prices else None # 后续业务逻辑
补充说明
如果你需要的是Education和Price关联的时间而非Price本身的创建时间,建议自定义多对多中间表,在中间表添加created_at字段,后续过滤基于中间表的时间字段即可,逻辑会更准确。
内容的提问来源于stack exchange,提问作者reza_khalafi
相关产品推荐
相关产品推荐

