我的网站搭建(第22天) 分页器优化

  • 发布时间:2016年7月5日 17:32
  • 作者:杨仕航
* 该文是基于Python2.7开发的,最新Python3.x和Django2.x视频教程可以前往 >> Django2.0视频教程

之前利用Django的Paginator分页器简单弄了分页功能,具体可以看我的网站搭建(第3天) Django分页器这篇文章。

这篇文章已经重新书写过了,之前写得比较糟糕,我自己都看不下去了。


随着发表的博文越来越多,会导致页码越来越多。页码显示的区域占用的空间也会越来越长。假如有20几页,30几页的页码都显示出来。那简直太惨不忍睹了,所以考虑优化一下。现在效果如下:


目前见得比较多的分页优化显示方式有三种:

1、"1 ... 5 6 7 8 ... 10" 用省略号表示隐藏中间部分的页码

2、"6 7 8 9 10 11 12 13 14 15" 连续显示多个页码,随当前页码变动页码范围(这种方式搜索引擎用得比较多)

3、下拉框选择页码(这个操作不直观,不选用这种)


博文的页码不会太多,所以我考虑采用第1种方式,再加上一个跳转页功能即可。

由于之前把分页功能写在helper文件夹中,封装成一个通用的方法(见我的网站搭建(第3天) Django分页器)。所以只需要改动这个方法,返回一个新的页码列表给html模版使用即可。


进一步分析一下。假如一共有8个页码,显示当前页上下两页和首尾的页码。罗列以下几种情况:

1、当前页是第1页

1 2 ... 8

2、当前页是第2页

1 2 3 ... 8

3、当前页是第4页

1 ... 3 4 5 ... 8

4、当前页是第7页

1 ... 6 7 8

5、当前页是第8页

1 ... 7 8


看上去似乎没问题,仔细分析,里面有些细节还是不够好。

当前页是第1页和第2页只是少了第3页没显示,当前页是第7页和第8页也是同样情况。这种情况可以考虑如果当前页是首尾的时候,多显示一页出来,让用户可选择的范围大一点。

另外当前页是第4页的时候,第一个省略号其实没什么意义,只是省略了一个页码。这个省略号同样也占用了一定空间,还不如把那个页码显示处理。

整理优化结果如下:

1、当前页是第1页

1 2 3 ... 8

2、当前页是第2页

1 2 3 ... 8

3、当前页是第4页

1 2 3 4 5 ... 8

4、当前页是第7页

1 ... 6 7 8

5、当前页是第8页

1 ... 6 7 8


大致分析清楚之后,开始写代码。要生成这种页码列表也有好几种方式。笨一点的方法就是判断当前页和首尾页码对比。我试了一下,挺麻烦的,要实现我上面优化的结果要判断很多种情况。最后改成下面的代码,直接对list处理。


通用的分页代码如下:

#coding:utf-8
from django.core.paginator import Paginator
from django.conf import settings

def getPages(request,objectlist):
    """get the paginator"""
    currentPage = request.GET.get('page', 1)
    paginator = Paginator(objectlist,settings.EACHPAGE_NUMBER)
    objectlist = paginator.page(currentPage)

    page_range = []
    current = objectlist.number     #当前页码
    page_all = paginator.num_pages  #总页数
    mid_pages = 3                   #中间段显示的页码数
    page_goto = 1                   #默认跳转的页码

    #获取优化显示的页码列表
    if page_all <= 2 + mid_pages:
        #页码数少于6页就无需分析哪些地方需要隐藏
        page_range = paginator.page_range
    else:
        #添加应该显示的页码
        page_range += [1, page_all]
        page_range += [current-1, current, current+1]

        #若当前页是头尾,范围拓展多1页
        if current == 1 or current == page_all:
            page_range += [current+2, current-2]

        #去掉超出范围的页码
        page_range = filter(lambda x: x>=1 and x<=page_all, page_range)

        #排序去重
        page_range = sorted(list(set(page_range)))

        #查漏补缺
        #从第2个开始遍历,查看页码间隔,若间隔为0则是连续的
        #若间隔为1则补上页码;间隔超过1,则补上省略号
        t = 1
        for i in range(len(page_range)-1):
            step = page_range[t]-page_range[t-1]
            if step>=2:
                if step==2:
                    page_range.insert(t,page_range[t]-1)
                else:
                    page_goto = page_range[t-1] + 1
                    page_range.insert(t,'...')
                t+=1
            t+=1

    #优化结果之后的页码列表
    paginator.page_range_ex = page_range
    #默认跳转页的值
    paginator.page_goto = page_goto

    return paginator,objectlist

加上比较的页码之后,可能出现超出范围和重复,再修正一次即可。

例如当前页是4,会得到[1, 3, 4, 5, 8]这个列表。接着进一步判断相邻元素相差多少。

相差1就是连续的,无需处理;

相差2就是前面提到的把省略号去掉,显示原本的页码;

相差3极其以上的,就加上省略号即可。

最终得到列表[1, 2, 3, 4, 5, '...', 8],再把这个列表传递给html模版即可。


在传递这个列表时候,不想再额外返回多一个对象。而paginator.page_range是只读的,不能赋值。

于是,赋值到paginator.page_range_ex中,该属性是自定义的,原本不存在。这个是利用python的特性,可以在对象额外加属性。这个属性可以在其他地方获取得到对应的值。

同样,那个跳转页码的默认值也是采用这种方式保存到分页器中。


按照我第3天写的分页器代码。响应博文列表的处理方法是不需改动的。这里也写出来(只写关键部分的代码),以便分析html模版代码:

#coding:utf-8
from django.shortcuts import render_to_response
from blog.models import Blog

from helper.paginator import getPages #封装的分页器方法

def index(request):
    """show blogs' list"""
    blogs = Blog.objects.all()
    pages,blogs=getPages(request,blogs)

    data = {}
    data["blogs"]=blogs
    data["pages"]=pages
        
    return render_to_response('blog/blog_list.html',data)


最后,修改一下html模版(html代码比较多,我只写关键部分):

<div class="blog_list_footer">
        <span class="blog_footer_tip">
            共{{pages.count}}篇博文。
            当前第{{blogs.number}}页,
            共{{pages.num_pages}}页
        </span>

        <div>
            <ul class="pagination">
                <li>
                    <a href="?page=
                        {% if blogs.has_previous %}
                            {{blogs.previous_page_number}}
                        {% else %}
                            {{blogs.number}}
                        {% endif %}
                    " aria-label="Previous">
                        <span aria-hidden="true">&laquo;</span>
                    </a>
                </li>

                {% for page in pages.page_range_ex %}
                    {% if page == '...' %}
                        <li><span>...</span></li>
                    {% else %}
                        <li><a href="?page={{page}}">{{page}}</a></li>
                    {% endif %}
                {% endfor %}

                <li>
                    <a href="?page=
                        {% if blogs.has_next %}
                            {{blogs.next_page_number}}
                        {% else %}
                            {{blogs.number}}
                        {% endif %}
                    " aria-label="Next">
                        <span aria-hidden="true">&raquo;</span>
                    </a>
                </li>
            </ul>

            {# 页码跳转的html代码 #}
            <ul class="pagination">
                <li id="page_goto">
                    <span >
                        <input type="text" value="{{paginator.page_goto}}" />
                    </span>
                </li>
                <li><a href="javascript:void(0);" onClick="page_goto();">Go</a></li>
            </ul>
        </div>
    </div>

这里的html模版和之前的基本一样,具体分析可以看之前的博文:我的网站搭建(第3天) Django分页器。不同的地方是在遍历的部分加了if判断。判断内容是不是省略号,若是省略号就用span标签。而且加上跳转页码的html代码。


这里还需要改动js代码:

<script type="text/javascript">
    {#设置凸显当前页码#}
    $(".pagination li a").each(function(){
        if($(this).text()=="{{blogs.number}}"){
            $(this).parent().addClass("active");
        }
    });

    {#页码跳转处理#}
    function page_goto(){
        var page = $("#page_goto input")[0].value;
        window.location.href = '?page=' + page;  //重定向
        return false;
    }
</script>

<style type="text/css">
    {#页码跳转的显示样式#}
    #page_goto span{
        padding: 6px 4px;
    }
    #page_goto input{
        border: none;
        padding: 0;
        width: 2em;
        text-align: center;
    }
</style>

最终效果如下:

上一篇:Python多线程使用win32com注意事项

下一篇:树莓派用开关三极管控制散热风扇

评论列表

智慧如你,不想发表一下意见吗?

新的评论

清空