Django Запрос, отличный от внешнего ключа - PullRequest
1 голос
/ 29 января 2020

С учетом этих моделей

class User(Model):
  pass

class Post(Model):
  by = ForeignKey(User) 
  posted_on = models.DateTimeField(auto_now=True)

Я хочу получить последние Post с, но не все с одного и того же User, у меня есть что-то вроде этого:

posts = Post.objects.filter(public=True) \
        .order_by('posted_on') \
        .distinct("by") 

Отличный не работает на mysql, мне интересно, есть ли другой способ сделать это? Я видел, как некоторые используют values(), но values не работает для меня, потому что мне нужно делать больше вещей с самими объектами

Ответы [ 2 ]

0 голосов
/ 29 января 2020

Поскольку не будет работать с MySQL в других полях, кроме модели id, возможен обходной путь с использованием Подзапрос :

from django.db.models import Subquery, OuterRef
...
sub_qs = Post.objects.filter(user_id=OuterRef('id')).order_by('posted_on')
# here you get users with annotated last post
qs = User.objects.annotate(last_post=Subquery(sub_qs[:1]))
# next you can limit the number of users

Также обратите внимание, что порядок на posted_on поле зависит от ограничений вашей модели - возможно, вам придется изменить его на -posted_on, чтобы сделать заказ из новейших сверху.

0 голосов
/ 29 января 2020

order_by должно соответствовать distinct(). В вашем случае вы должны делать это:

posts = Post.objects.filter(public=True) \
        .order_by('by') \
        .distinct('by') 

.distinct([*fields]) работает только в PostgresSQL.

Для MySql Engine. Это MySQL документация в Django:

Вот разница. Для обычного вызова Different () база данных сравнивает каждое поле в каждой строке, определяя, какие строки различны. Для вызова Different () с указанными именами полей база данных будет сравнивать только указанные имена полей.

Для MySql обходной путь может быть следующим:

from django.db.models import Subquery, OuterRef


user_post = Post.objects.filter(user_id=OuterRef('id')).order_by('posted_on')            
post_ids = User.objects.filter(related_posts__isnull=False).annotate(post=Subquery(user_post.values_list('id', flat=True)[:1]))).values_list('post', flat=True)
posts = Post.objects.filter(id__in=post_ids)
...