Django manytomany查询奇怪的行为

时间:2016-12-14 08:20:55

标签: mysql django

我有以下型号:

class Post(Model):
    word = TextField()
    subscribers = ManyToManyField(User, related_name='subscribed',    through='Subscription')

class Subscription(Model):
    post = ForeignKey(Post)
    subscriber = ForeignKey(User)
    date_subscribed = DateTimeField(default=timezone.now)

    class Meta:
        ordering = ('-date_subscribed', )
        unique_together = (('post', 'subscriber'))

我想要做的是选择所有帖子,按订阅者数量排序,如果订阅者数量相等,则按上一个date_subscribed排序。

我的输入数据:

post1 = Post(text="post1")
post2 = Post(text="post2")
post3 = Post(text="post3")
post4 = Post(text="post4")

user1 = User(username="user1")
user2 = User(username="user2")
user3 = User(username="user3")
user4 = User(username="user4")

Subscription.objects.create(post=post1, user=user1)
Subscription.objects.create(post=post2, user=user1)
Subscription.objects.create(post=post3, user=user1)
Subscription.objects.create(post=post3, user=user2)
Subscription.objects.create(post=post3, user=user3)
Subscription.objects.create(post=post3, user=user4)
Subscription.objects.create(post=post4, user=user1)
Subscription.objects.create(post=post4, user=user2)
Subscription.objects.create(post=post4, user=user3)

此查询按预期工作,但不按date_subscribed排序:

Post.objects.annotate(s_count=Count('subscribers')).order_by('-s_count')

当我写:

Post.objects.annotate(s_count=Count('subscribers')).order_by('-s_count', '-subscription__date_subscribed') 

我得到了奇怪的结果,我真的不明白这种行为。对于上述数据,它会输出s_count=1的所有帖子。

为什么s_count是1?此外,还有如何按上次date_subscribed正确订购?

UPD: 还有一个问题。为什么Post.objects.annotate(s_count=Count('subscribers')).order_by‌​('-s_count', '-subscription__date_subscribed').count()在订阅中提供4而不是行数?

1 个答案:

答案 0 :(得分:1)

由于SubscriptionPostSubscriber之间m2m关系的直通表,当您在Subscription模型本身的字段上订购时,所有帖子都显示为结果集中的各个行以及您获得s_count=1的原因,因为每个具有特定订阅者的帖子都是唯一的。

您需要使用所有Post中的最新date_subscribed注释subscribers个对象,然后在带注释的字段中进行排序:

posts = Post.objects.annotate(
            s_count=Count('subscribers'),
            s_date_max=Max('subscription__date_subscribed')
        ).order_by('-s_count', '-s_date_max')

下一个问题的更新:

如果您使用count()方法,则会返回Posts的数量。您可以看到它与len(queryset.values_list('s_count', 'subscription__date_subscribed'))的计数不同,因为此时已在结果集中提取日期的各个值。