Как я могу отсортировать по идентификатору ManyToManyField в Django? - PullRequest
8 голосов
/ 15 декабря 2009

У меня есть ManyToManyField в пользовательском объекте, и он используется для отображения пользователей, за которыми следует этот пользователь. Я пытаюсь показать подмножество списка тех, за кем они следили совсем недавно. Есть ли хитрость в .order_by (), которая позволит мне сортировать по идентификатору ManyToManyField? Данные есть, верно?

# (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 ]

4 голосов
/ 14 августа 2014

Я только что нашел способ сделать это без необходимости создавать класс для отношений. Он опирается на функцию 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';
4 голосов
/ 16 февраля 2017

На самом деле (по крайней мере, в 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")

Многие другие решения предлагают создать настраиваемую сквозную таблицу и добавить поле, но если вы просто хотите отсортировать по идентификатору автоматически сгенерированной сквозной таблицы, то в этом нет необходимости.

1 голос
/ 14 марта 2018

Проверено с 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)
0 голосов
/ 16 декабря 2009

Я не уверен, что вы можете добиться этого с помощью обычного 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)

Затем вы можете создать следующие отношения в оболочке следующим образом.

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

Вы можете создать набор запросов из FollowerRelationship объектов, где Алиса является последователем, упорядоченным по идентификатору таблицы соединения, со строкой:

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

Обратите внимание, что вам нужно пройтись по FollowerRelationship объектам, чтобы получить "1011 *" в отношениях.

Возможно, вы захотите взглянуть на Дополнительные поля отношений «многие ко многим» в документах Django, в которых описано, как указать промежуточную модель в отношении «многие ко многим».

...