【问题标题】:Django pagination overhead with sortingDjango分页开销与排序
【发布时间】:2011-10-02 23:26:02
【问题描述】:

我尝试使用一些排序选项在 Django 中实现分页结构,但是我不知道如何才能正确地做到这一点。

views.py

def search(request):
    eList = Employer.objects.filter(eminence__lt=4).order_by('-eminence')

    paginator = Paginator(eList, 3) # Show 3 contacts per page

    page = request.GET.get('page')
    try:
        employerList = paginator.page(page)
    except PageNotAnInteger:
        employerList = paginator.page(1)
    except EmptyPage:
        employerList = paginator.page(paginator.num_pages)

return render_response(request, 'employer/search.html', {'employerList':employerList})

search.html

<div class="pagination">
    <span class="step-links">
        {% if employerList.has_previous %}
            <a href="?page={{ employerList.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current"> Page {{ employerList.number }} of {{ employerList.paginator.num_pages }}.</span>
        {% if employerList.has_next %}
            <a href="?page={{ employerList.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

这个例子效果很好,但是正如你所看到的,对于每个导航,我都需要获取所有 Employer 对象。之后Paginator 根据页码处理查询中的对象。但是,我认为分页应该在查询期间完成,并且只根据页码获取我想要的 X 个对象。当然,我可以以这种方式修改代码,但我不知道为什么人们使用Paginator,尽管有这样的开销。我可能会忽略一个细节...

我的第二个问题是如何应用排序我的列表?我是否应该修改我的url 并传递排序方法和页码,并根据它们通过排序方法获取查询并将其提供给Paginator

这对我来说似乎合理且很好,但我只是想知道在 django 中是否有更好的方法。

谢谢

【问题讨论】:

    标签: django sorting pagination


    【解决方案1】:

    其实 Paginator 依赖于 QuerySet 的切片。切片取决于特定的数据库 - 如果 django db 适配器支持它(例如,将切片限制传输到 SQL 查询 - 类似于 LIMIT x OFFSET y 用于 mysql 或其他东西用于其他数据库)。如果支持,则查询仅获取给定页面的行。
    我相信对于 MySQL,这个功能现在被禁用了(我正在研究 Django 1.3),因为 OF​​FSET 子句的性能问题。

    【讨论】:

      【解决方案2】:

      我强烈建议使用无限分页。这是一个awsum应用程序。 在这里查看示例项目:https://github.com/venkasub/endless_pagination_example

      【讨论】:

        【解决方案3】:

        Django 在延迟加载数据方面非常聪明,这使得这些查询非常高效。让我们来看看您的请求会发生什么...

        eList = Employer.objects.filter(eminence__lt=4).order_by('-eminence')
        ## No database query.
        
        paginator = Paginator(eList, 3)
        ## No database query.
        
        employerList = paginator.page(2)
        ## SELECT COUNT(*) FROM `yourproject_employer`
        ## WHERE `yourproject_employer`.`eminence` < 4
        
         # Force iteration.  Same as looping over results:
        foo = list(employerList.object_list)
        ## SELECT * FROM `yourproject_employer`
        ## WHERE `yourproject_employer`.`eminence` < 4
        ## ORDER BY `yourproject_employer`.`eminence` DESC
        ## LIMIT 3 OFFSET 3
        

        至于您的排序问题,我想说只需按照您的建议修改 GET 参数。只需非常小心地将其传递给数据库。例如,我会列出可能的排序并对此进行验证。这也意味着您不必公开数据库的内部工作原理。

        VALID_SORTS = {
            "pk": "pk",
            "pkd": "-pk",
            "em": "eminence",
            "emd": "-eminence",
        }
        DEFAULT_SORT = 'pk'
        def search(request):
            sort_key = request.GET.get('sort', DEFAULT_SORT) # Replace pk with your default.
            sort = VALID_SORTS.get(sort_key, DEFAULT_SORT)
        
            eList = Employer.objects.filter(eminence__lt=4).order_by(sort)
        

        【讨论】:

        • 我知道 django 开发人员比我更聪明 :) 感谢我在应用“if else”控制时提出的安全建议,但它似乎更优雅的解决方案
        • 实际上我昨天刚刚在我的网站上实现了 90%,所以我很乐意提供帮助。 =-]
        猜你喜欢
        • 2016-12-27
        • 2018-12-12
        • 2014-12-31
        • 1970-01-01
        • 2015-10-08
        • 2014-02-07
        • 1970-01-01
        • 1970-01-01
        • 2017-07-09
        相关资源
        最近更新 更多