Django查询如何过滤多对多关联中首个艺术家符合条件的歌曲
解决方案
要实现按关联的首个艺术家过滤,首先需要明确多对多关联中「第一个」的排序规则(必须和song.artists.first()的排序逻辑一致,默认是关联表的自增ID升序),以下是两种可行方案:
方案1:数据库层面查询(推荐,性能更好,支持分页等操作)
使用Django的窗口函数+子查询实现,适合数据量较大的场景:
from django.db.models import Subquery, OuterRef, Window, F from django.db.models.functions import RowNumber # 构造子查询:获取每首歌的首个关联艺术家ID first_artist_subquery = Artist.objects.filter( song__id=OuterRef('pk') ).annotate( # 按歌曲分组,给同一首歌的关联艺术家按规则排序并编号 artist_order=Window( expression=RowNumber(), partition_by=F('song__id'), # 此处排序规则必须和你调用 song.artists.first() 的排序完全一致 # 如果是按关联时间先后,默认可以写 order_by=F('id').asc() # 如果多对多有中间表且有排序字段,可替换为 order_by=F('中间表名__排序字段').asc() order_by=F('id').asc() ) ).filter( artist_order=1 # 取编号为1的首个艺术家 ).values('id')[:1] # 过滤符合条件的歌曲 target_songs = Song.objects.annotate( first_artist_id=Subquery(first_artist_subquery) ).filter( first_artist_id__country__code__in=['NL', 'BE'] )
方案2:Python层面过滤(仅适合小数据量场景)
直接遍历所有歌曲,在内存中判断首个艺术家的国家,代码更简单但数据量大时性能差:
target_songs = [] # 预加载关联艺术家避免N+1查询 for song in Song.objects.prefetch_related('artists'): first_artist = song.artists.first() if first_artist and first_artist.country and first_artist.country.code in ('NL', 'BE'): target_songs.append(song)
注意事项
- 如果你自定义了
Artist模型的Meta.ordering,或者多对多关联有自定义排序规则,必须修改方案1中子查询的order_by参数,保证和first()的排序逻辑一致,否则查询结果会不符 - 无关联艺术家、或首个艺术家无国家信息的歌曲会默认被过滤,如有需要可以额外调整过滤条件保留对应数据
内容的提问来源于stack exchange,提问作者A. Nurb
相关产品推荐
相关产品推荐

