django queryset не может отличить результат с использованием подзапроса? - PullRequest
0 голосов
/ 30 апреля 2020

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

Проблема в том, что два результата qset в моем коде разные. qset1 правильный, но qset2 неправильный, потому что он не исключает тех же пользователей.

Мой вопрос: что мне делать, если я не хочу использовать Raw SQL, как qset1, и хочу получить правильный результат ?

Мой код выглядит следующим образом:

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

#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";
    '''  
...