Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort by the id of a ManyToManyField in Django?

I've got a ManyToManyField in a user object and it's used to map the users that user is following. I'm trying to show a subset list of who they have most recently followed. Is there a trick in .order_by() that will allow me to sort by the id of the ManyToManyField? The data is there, right?

# (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")

That will give me a list of the users the user is following but ordered by when they joined. I want the order of the following list to be in order of when the user followed them.

like image 248
Tim Trueman Avatar asked Dec 15 '09 03:12

Tim Trueman


People also ask

What is ManyToManyRel in Django?

ManyToManyRel is used by the ManyToManyField to implement the relationship object for the Field base class which it extends.


2 Answers

In fact (at least in Django 1.10), you don't need to use the extra feature but instead can just order by the field directly. Just use the automatically created through table name followed by ".id" as an argument to order_by. E.g.

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

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

For this particular question:

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

Many other solutions suggest creating a custom through table and adding a field but if you just want to sort by the id of the automatically generated through table then this is not necessary.

like image 138
User Avatar answered Oct 05 '22 03:10

User


I just found a way to do this without having to create a class for the relationship. It relies on the extra feature that lets you add additional columns to output. In your example it would look like:

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

Notice that appname_user_user_following is the name of the relationship table Django creates under the covers. It's deterministic and something you can get and set via meta-mechanisms, but it's pretty much safe to hardcode.

Here's an example of the SQL that's being created under the covers with fake table and columns names:

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';
like image 27
Ry4an Brase Avatar answered Oct 05 '22 02:10

Ry4an Brase