Django过滤,分页和注释分页结果

时间:2013-08-01 01:32:05

标签: python django postgresql

我有对象Reports和ReportSubscriber,我想计算报告的订阅者数量。

一种解决方案是注释。我有很多报告所以注释所有这些都需要大约6秒钟,所以我想也许最好在分页后进行注释:

filter_search = ReportFilter(request.GET, queryset=Report.objects.filter(
        created_at__gt=start_date,
        created_at__lte=end_date,
        is_confirmed__exact=True,
    ).annotate(sub_count=Count("reportsubscriber")).order_by('-sub_count'))

paginator = Paginator(filter_search, 20)

result = paginator.page(1).object_list.annotate(
                sub_count=Count("reportsubscriber"))

它有效,但是花了相同的时间,当我检查查询时,它实际上仍然遍历report_subscriber表中的所有行。所以我尝试使用.extra()

filter_search = ReportFilter(request.GET, queryset=Report.objects.filter(
            created_at__gt=start_date,
            created_at__lte=end_date,
            is_confirmed__exact=True,
        ))

paginator = Paginator(filter_search, 20)
paged_reports = paginator.page(1)

result = filter_search.qs.extra(
            select={
                'sub_count': 'SELECT COUNT(*) FROM reports LEFT OUTER JOIN report_subscribers  \
                             ON (reports.id = report_subscribers.id) \
                             WHERE reports.id = report_subscribers.id \
                             AND report_subscribers.report_id IN %s \
                            ' % "(%s)" % ",".join([str(r.id) for r in paged_reports.object_list])
            },
            order_by=['sub_count']
        )

但这仍然无效。我为所有报告提供了一个静态订阅者数量。我错过了什么,也许有更好的方法来实现这一目标?感谢

3 个答案:

答案 0 :(得分:1)

我不能给你一个明确的答案,我相信你的问题是,即使在分页时,你的整个查询也必须执行,以便分页器知道有多少页面。我认为你最好在分页之前摆脱注释:

filter_search = ReportFilter(request.GET, queryset=Report.objects.filter(
        created_at__gt=start_date,
        created_at__lte=end_date,
        is_confirmed__exact=True,
    ).order_by('-sub_count'))

paginator = Paginator(filter_search, 20)

result = paginator.page(1).object_list.annotate(
                sub_count=Count("reportsubscriber"))

我相信您的示例object_list是一个可以annotate的查询集,但如果它只是list个对象,您可以使用以下内容对每个结果页面进行注释:

pageIds = [report.id for report in paginator.page(1).object_list]
result = Report.objects.filter(id__in=pageIds).annotate(
                sub_count=Count("reportsubscriber"))

但这都是在黑暗中拍摄的。你所做的一切都看起来太疯狂,所以除非你的数据集很大,否则我只能想象你的问题是一个索引不佳的查询。您真的想要分析正在生成的实际查询。您可以通过从项目Django shell执行给定的start_dateend_data来获取SQL:

Report.objects.filter(
        created_at__gt=start_date,
        created_at__lte=end_date,
        is_confirmed__exact=True,
    ).order_by('-sub_count').query

然后使用EXPLAIN从数据库的PSQL命令行运行相同的查询。你必须做一些阅读才能弄清楚如何解释结果。

答案 1 :(得分:0)

好的,明白了。我选错了桌子。

所以我改变了.extra(),现在每页只有COUNTing:

result = filter_search.qs.extra(
            select={
                'sub_count': 'SELECT COUNT(*) FROM report_subscribers \
                             WHERE report_subscribers.report_id = reports.id\
                             AND report_subscribers.report_id IN %s \
                            ' % "(%s)" % ",".join([str(r.id) for r in paged_reports.object_list])

            }
        )

但现在我无法按sub_count排序,因为我没有所有值。好吧,也许没有任何其他方法可以不计算数据库中的所有或实际存储计数

答案 2 :(得分:0)

这就是我使用Paginator进行过滤和基于类的视图的方法:

from django.core.paginator import Paginator, EmptyPage, InvalidPage

class BaseTemplateView(TemplateView):
    """
    Abstract View to populate NavBar context
    """
    def get_context_data(self, **kwargs):
        context = super(BaseTemplateView, self).get_context_data(**kwargs)
        context['ideas'] = Idea.objects.all()
        return context

class IdeaView(BaseTemplateView):

    template_name = 'products/idea.html'

    def get(self, request, *args, **kwargs):
        ideas = Idea.objects.all()
        idea = get_object_or_404(Idea, slug=kwargs['slug'])
        products = Product.objects.filter(ideas=idea)
        products_list = products
        paginator = Paginator(products_list, 12)
        try:
            page = int(request.GET.get('page', '1'))
        except:
            page = 1

        try:
            products = paginator.page(page)
        except(EmptyPage, InvalidPage):
            products = paginator.page(paginator.num_pages)

        return render_to_response(self.template_name,
            {'ideas': ideas, 'idea': idea, 'products': products},
            context_instance=RequestContext(request))