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))
我想汇总使用同一视频资源的工作量,以及基于不同用户数的用户量。我为此尝试了两种方法,但得到的结果相互矛盾。
问题是:我代码中的两个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))