django 查询集无法区分使用子查询的结果?

django queryset can not distinct the result with using Subquery?

我想汇总使用同一视频资源的工作量,以及基于不同用户数的用户量。我为此尝试了两种方法,但得到的结果相互矛盾。

问题是:我代码中的两个qset结果不一样。 qset1是正确的,qset2是错误的,因为它没有排除相同的用户。

我的问题:不想像qset1那样使用RawSQL,想要正确的结果怎么办?

我的代码如下:

#models concerned
class Video(models.Model):
    name = models.CharField(max_length=100, verbose_name=_('video name'))

class Work(models.Model):
    video = models.ForeignKey(Video, on_delete=models.CASCADE, related_name='works_of_video', verbose_name=_('video'))
    maker = models.ForeignKey(User, on_delete=models.CASCADE, related_name='works_of_maker', verbose_name=_('maker'))

class ShareRecord(models.Model):
    work = models.ForeignKey(Work, on_delete=models.CASCADE, related_name='share_records_of_work', verbose_name=_('work shared'))
    sharer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='share_records_of_sharer', verbose_name=_('sharer'))
    share_date = models.DateField(default=timezone.now, verbose_name=_('share date')) 


#rawsql for get_queryset()
video_sql = u'''SELECT COUNT(*) AS "num"
            From (SELECT DISTINCT maker_id
                FROM "video_manage_work" U0
                INNER JOIN "video_manage_demouser" U2 ON (U0."maker_id" = U2."id")
                WHERE (U0."video_id" = ("video_manage_video"."id") AND U2."gender" IN (%s))
            )'''

#views concerned
def get_queryset(request):
    what_gender = (0, 1)
    vall = Video.object.all()
    works = Work.objects.filter(video=OuterRef('pk')).filter(maker__gender__in=what_gender)
    works_makers = works.values('maker','video').distinct()

    # the next annotate use RawSQL of video_sql
    qset1 = vall.annotate(works_num=Subquery(works.values('video').annotate(num=Count('*')).values('num'), output_field=IntegerField()))\
        .annotate(makers_num=RawSQL(video_sql, (gender_str,)))

    # the next annotate use Subquery that try to distinct queryset that excluded the same users
    qset2 = vall.annotate(works_num=Subquery(works.values('video').annotate(num=Count('*')).values('num'), output_field=IntegerField()))\
        .annotate(makers_num=Subquery(works_makers.values('video').annotate(num=Count('*')).values('num'), output_field=IntegerField()))

    return render(request, 'video_manage/video_data.html')

    '''
    ######sql sentenc of qset2 created by django:
    SELECT "video_manage_video"."id",
           "video_manage_video"."name",

      (SELECT COUNT(*) AS "num"
       FROM "video_manage_work" U0
       INNER JOIN "video_manage_demouser" U2 ON (U0."maker_id" = U2."id")
       WHERE (U0."video_id" = "video_manage_video"."id"
              AND U2."gender" IN (0,
                                  1))
       GROUP BY U0."video_id") AS "works_num",
      (SELECT COUNT(*) AS "num"
          From (SELECT DISTINCT maker_id
           FROM "video_manage_work" U0
           INNER JOIN "video_manage_demouser" U2 ON (U0."maker_id" = U2."id")
           WHERE (U0."video_id" = ("video_manage_video"."id")
                  AND U2."gender" IN (0,
                                      1))
            )
       ) AS "makers_num"
    FROM "video_manage_video";
    '''  

找一个简单的方法解决,related_name可以链接在一起做聚合表达式:

qset=vall.annotate(works=Count('works_of_video',distinct=True))\
    .annotate(makers=Count('works_of_video__maker',distinct=True))\
    .annotate(share_works=Count('works_of_video__share_records_of_work',distinct=True))\
    .annotate(share_sharers=Count('works_of_video__share_records_of_work__sharer',distinct=True))