如何在Django中按ManyToManyField的id排序?
我在用户对象里有一个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 个回答
我刚找到一种方法,可以在不创建关系类的情况下实现这个功能。这个方法利用了一个叫做 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';
测试使用的是 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)
其实(至少在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排序,那其实并不需要这样做。