You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中使用__In过滤时保留字符串ID的顺序?

Django过滤后保持输入列表顺序的正确实现

问题核心

使用video__in过滤Video模型时,需让返回结果与传入的字符串类型video_ids列表顺序完全一致,此前因错误使用主键id字段进行Case/When排序,导致类型不匹配报错。

正确实现方案

核心是基于存储字符串ID的video字段构建排序规则,而非主键id。通过Case/When为每个传入的video_id指定对应的排序位置,再用order_by应用该规则即可。

修改后的完整代码

def get_video_objects(video_ids, count):
    '''
    video_ids are already in the correct order, filter should return rows in the same order
    '''
    videos = Video.objects.all()
    # 基于video字段创建排序映射,匹配每个video_id并分配顺序位置
    video_id_order = Case(*[When(video=vid, then=pos) for pos, vid in enumerate(video_ids)])
    # 先过滤再按指定顺序排序
    results = videos.filter(video__in=video_ids).order_by(video_id_order)
    results = list(results[:int(count)].values('video', 'title', 'comment_count', 'likes'))
    return results

关键说明

  • 替换了Case/When中的匹配字段:从主键id(整数类型)改为video(字符串类型),解决了"Field 'id' expected a number but got 'xxx'"的类型错误。
  • enumerate(video_ids)为每个video_id分配了传入时的顺序位置(pos),order_by(video_id_order)会按照这个位置值从小到大排序,最终结果顺序与video_ids完全一致。
  • 该方案兼容所有Django支持的数据库(MySQL、PostgreSQL、SQLite等)。

PostgreSQL专属优化方案

如果你的项目使用PostgreSQL,可以利用其原生的array_position函数简化排序逻辑,大列表场景下性能更优:

from django.db.models import Func, F

def get_video_objects(video_ids, count):
    videos = Video.objects.all()
    results = videos.filter(video__in=video_ids).annotate(
        # 计算每个video在传入列表中的位置
        position=Func(video_ids, F('video'), function='array_position')
    ).order_by('position')
    results = list(results[:int(count)].values('video', 'title', 'comment_count', 'likes'))
    return results

内容的提问来源于stack exchange,提问作者Patt Mehta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 05:35:10