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

Tim*_*man 9 python django sql-order-by django-models manytomanyfield

我在用户对象中有一个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")
Run Code Online (Sandbox Code Playgroud)

这将为我提供用户所关注的用户列表,但在他们加入时按订单排序.我希望以下列表的顺序按用户跟随它们的顺序排列.

Ry4*_*ase 5

我刚刚找到了一种无需为关系创建类的方法。它依赖于extra允许您向输出添加其他列的功能。在您的示例中,它看起来像:

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

请注意,这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';
Run Code Online (Sandbox Code Playgroud)


Use*_*ser 5

实际上(至少在 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 进行排序,则没有必要。


Duš*_*ďar 5

测试用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)
Run Code Online (Sandbox Code Playgroud)


Ala*_*air 0

我不确定你是否可以通过常规ManytoManyField. 您可以尝试显式定义中间模型。

注意:未经测试的代码!

class Person(models.Model)
    name = models.CharField(max_length=30)

class FollowerRelationship(models.Model)
    follower = models.ForeignKey(Person, related_name = following_set)
    following = models.ForeignKey(Person, related_name = follower_set)
Run Code Online (Sandbox Code Playgroud)

然后,您可以在 shell 中创建以下关系,如下所示。

# Create Person objects
>>> a = Person(name="Alice")
>>> a.save()
>>> b = Person(name="Bob")
>>> b.save()
>>> c = Person(name="Chris")
>>> c.save()

# Let Alice follow Chris and Bob 
>>> FollowerRelationship.objects.create(follower=a, following=c)
>>> FollowerRelationship.objects.create(follower=a, following=b)
Run Code Online (Sandbox Code Playgroud)

您可以创建一个FollowerRelationship对象查询集,其中 Alice 是关注者,按连接表的 id 排序,使用以下行:

>>> qs = FollowerRelationship.objects.filter(follower=a).order_by('id')
>>> [fr.following for fr in qs]
Run Code Online (Sandbox Code Playgroud)

请注意,您必须循环遍历对象,才能获得关系中的FollowerRelationship“关注” 。Person

您可能还想查看Django 文档中多对多关系的额外字段,它描述了如何在多对多关系中指定中间模型。