Django之分页器

Django自带分页器的实现

介绍

Django提供了一个新的类来帮助你管理分页数据,这个模块存放在django.core.paginator.py。
其中有两个核心类,一个是Paginator类,另一个是Page类。

Paginator类
初始化操作
class Paginator(object):
    def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
        self.object_list = object_list
        self.per_page = int(per_page)
        self.orphans = int(orphans)
        self.allow_empty_first_page = allow_empty_first_page
        self._num_pages = self._count = None

解释:

  • object_list:可以是列表,元组,查询集或其他含有 count() 或 len()方法的可切片对象。对于连续的分页,查询集应该有序,例如有order_by()项或默认ordering参数。
  • per_page:每一页中包含条目数目的最大值,不包括独立成页的那页。(见下面 orphans参数解释)。
  • orphans=0:当你使用此参数时说明你不希望最后一页只有很少的条目。如果最后一页的条目数少于等于orphans的值,则这些条目会被归并到上一页中(此时的上一页变为最后一页)。
  • allow_empty_first_page=True: 默认允许第一页为空。
类方法:
  • Paginator.page(number):根据参数number返回一个Page对象。(number为1的倍数)
类属型:
  • Paginator.count:所有页面对象总数,即统计object_list中item数目。
    当计算object_list所含对象的数量时, Paginator会首先尝试调用object_list.count()。
    如果object_list没有 count() 方法,Paginator 接着会回退使用len(object_list)。
  • Pagnator.num_pages:页面总数。
  • pagiator.page_range:页面范围,从1开始,例如[1,2,3,4]。
Page类
初始化操作
class Page(collections.Sequence):
    def __init__(self, object_list, number, paginator):
        self.object_list = object_list
        self.number = number
        self.paginator = paginator
类方法
  • Page.has_next() 如果有下一页,则返回True。
  • Page.has_previous() 如果有上一页,返回 True。
  • Page.has_other_pages() 如果有上一页或下一页,返回True。
  • Page.next_page_number() 返回下一页的页码。如果下一页不存在,抛出InvlidPage异常。
  • Page.previous_page_number() 返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
  • Page.start_index() 返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。
    比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
  • Page.end_index() 返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。
    比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。
类属型
  • Page.object_list 当前页上所有对象的列表。
  • Page.number 当前页的序号,从1开始。
  • Page.paginator 相关的Paginator对象。
异常处理
  • InvalidPage(Exception): 异常的基类,当paginator传入一个无效的页码时抛出。
    Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。通常,捕获InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:
  • exception PageNotAnInteger,当向page()提供一个不是整数的值时抛出。
    exception EmptyPage,当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。
    这两个异常都是InalidPage的子类,所以可以通过简单的except InvalidPage来处理它们。
使用方式
  • views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request):
    contact_list = Contacts.objects.all()  # 获取所有contacts,假设在models.py中已定义了Contacts模型
    paginator = Paginator(contact_list, 25) # 每页25条
    page = request.GET.get('page')
    try:
        contacts = paginator.page(page) # contacts为Page对象!
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)
    return render(request, 'list.html', {'contacts': contacts})
  • *.html
{% for contact in contacts %}
    {# Each "contact" is a Contact model object. #}
    {{ contact.full_name|upper }}<br />
    ...
{% endfor %}

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

        <span class="current">
            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
        </span>

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

或者另一种形式

<div id="pagination">
    <ul id="pagination-flickr">
    {% if article_list.has_previous %}
    <li class="previous"><a href="?page={{ article_list.previous_page_number }}{% if request.GET.year %}&year={{ request.GET.year }}{% endif %}{% if request.GET.month %}&month={{ request.GET.month }}{% endif %}{% if request.GET.cid %}&cid={{ request.GET.cid }}{% endif %}">&laquo;上一页</a></li>
    {% else %}
    <li class="previous-off">&laquo;上一页</li>
    {% endif %}

     <li class="active">{{ article_list.number }}/{{ article_list.paginator.num_pages }}</li>
    {% if article_list.has_next %}
      <li class="next"><a href="?page={{ article_list.next_page_number }}{% if request.GET.year %}&year={{ request.GET.year }}{% endif %}{% if request.GET.month %}&month={{ request.GET.month }}{% endif %}{% if request.GET.cid %}&cid={{ request.GET.cid }}{% endif %}">下一页 &raquo;</a></li>
    {% else %}
      <li class="next-off">下一页 &raquo;</li>
    {% endif %}
   </ul>
</div>
存在问题
描述

上面这种分页方式,在数据量较小的时候是没有效率问题,但是随着数据量的不断增大,查询速度越来越慢。


Django分页器优化的实现

原理

利用Django查询集的惰性查询特性

实现
def owner_page(request, obj):
    """
    # 自定义分页器
    :param request: request请求
    :param obj: 分页对象
    :return: 所在页码的对象集,所有页码,当前页码,分页对象的总数
    """
    current_page = 1
    all_page = 1
    page_type = ''
    try:
        current_page = int(request.GET.get('cur', '1'))
        all_page = int(request.GET.get('all', '1'))
        page_type = str(request.GET.get('action', ''))  # 向前翻页还是向下翻页
    except ValueError:
        current_page = 1
        all_page = 1
        page_type = ''

    if page_type == 'next':
        current_page += 1
    elif page_type == 'previous':
        current_page -= 1
    if isinstance(obj, list):
        count = len(obj)
    else:
        count = obj.count()
    start = (current_page - 1) * PAGE_SIZE
    end = current_page * PAGE_SIZE
    data = obj[start:end]

    if current_page == 1 and current_page == 1:  # 标记1
        all_page = math.ceil(count / PAGE_SIZE)
    return data, all_page, current_page, count
使用方式
  • views.py
def get_all_goods(request):
    user_id = request.session.get('user_id')
    goods = Good.objects.get_user_goods(user_id=user_id)
    data, all_page, current_page, count = owner_page(request, goods)
    return render(
        request,
        "goods_all_list.html",
        {'data': data, 'allPage': all_page, 'curPage': current_page, 'count': count}
    )
  • page.html
<div id="pages" class="text-left">
    <nav>
        <ul class="pagination">
            <li class="step-links">
                {% ifnotequal curPage 1 %}
                <a href="?cur={{ curPage }}&&all={{ allPage }}&&action=previous">
                    上一页
                </a>
                {% endifnotequal %}

                <span class="current">第{{ curPage }}&nbsp;&nbsp;/&nbsp;&nbsp;{{ allPage }}页</span>

                {% ifnotequal curPage allPage %}
                <a href="?cur={{ curPage }}&&all={{ allPage }}&&action=next">
                    下一页
                </a>
                {% endifnotequal %}
            </li>
            <li><span class="[object Object]"
                      style="color:blank;sex:black;border:1px solid #ddd;">共{{ count }}条记录</span></li>
        </ul>
    </nav>
</div>
  • goods_all_list.html
...
{% include "page.html" %}
...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 当页面因需要展示的数据条目过多,导致无法在一个页面全部显示。这时,页面经常会采用分页形式进行展示,然后每页显示 2...
    猴哥爱读书阅读 33,398评论 4 20
  • 模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。软件设计中通常用耦合度和内聚...
    riverstation阅读 2,059评论 0 8
  • 方法一:使用pure_pagination进行分页 1. pure_pagination介绍 pure_pagin...
    Jlan阅读 1,116评论 0 7
  • 项目设置 DEBUG 一个布尔型用来开启或关闭项目的debug模式。如果设置为True,当你的应用抛出一个未...
    lijun_m阅读 752评论 0 0
  • 一、Django框架前言知识: 1、C/S和B/S的区别: C/S结构软件:客户端/服务端软件,即客户端要自己下载...
    月下独酌123阅读 4,482评论 0 36