如何在Django中按ManyToManyField的id排序?

13 投票
4 回答
5317 浏览
提问于 2025-04-15 17:01

我在用户对象里有一个ManyToManyField,用来记录这个用户关注了哪些人。我想显示一个列表,列出他们最近关注的那些人。请问在.order_by()里有没有什么技巧,可以让我按照ManyToManyField的ID来排序呢?数据是存在的,对吧?

# (people the user is following)
following = models.ManyToManyField(User, related_name="following", blank=True)

theuser.following.filter(user__is_active=True).order_by("user__id")

这样做会给我一个用户关注的人的列表,但这个列表是按照他们加入的时间排序的。我想要的,是关注列表按照用户关注他们的时间来排序。

4 个回答

5

我刚找到一种方法,可以在不创建关系类的情况下实现这个功能。这个方法利用了一个叫做 extra 的特性,它可以让你在输出中添加额外的列。在你的例子中,它看起来会像这样:

theuser.following.filter(user__is_active=True)\
    .extra(select={'creation_seq': 'appname_user_user_following.id'})\
    .order_by("creation_seq")

注意,appname_user_user_following 是Django在后台创建的关系表的名称。这个名称是固定的,你可以通过一些元机制来获取和设置,但基本上可以放心直接写上这个名字。

下面是一个在后台生成的SQL示例,使用的是假设的表和列名:

SELECT (appname_user_user_following.id) AS `creation_seq`, `appname_user`.`id`
FROM `appname_user` INNER JOIN `appname_user_user_following` ON
(`appname_user`.`id` = `appname_user_user_following`.`user_id`) WHERE
`appname_user_user_following`.`user_followed_id` = 1  ORDER BY `creation_seq` ASC';
6

测试使用的是 Django 1.11.10

你不需要死记硬背关系表的名字(如何读取模型实例的数据库表名?)。

所以对 @Ry4an Brase 的回答可以更新成这样:

recently_followed = '-{}.id'.format(theuser.following.through._meta.db_table)  
theuser.following.filter(user__is_active=True).order_by(recently_followed)
10

其实(至少在Django 1.10中),你不需要使用extra这个功能,而是可以直接按字段排序。只需使用自动创建的中间表名称,后面加上“.id”作为order_by的参数。比如:

pizza.toppings.all().order_by('appname_pizza_toppings.id')

article.tags.all().order_by('appname_article_tags.id')

针对这个具体的问题:

theuser.following.filter(user__is_active=True)\ .order_by("appname_user_user_following.id")

很多其他的解决方案建议创建一个自定义的中间表并添加一个字段,但如果你只是想按自动生成的中间表的id排序,那其实并不需要这样做。

撰写回答