Job Search Engine


936 浏览 5 years, 10 months

2.10 分页功能

版权声明: 转载请注明出处 http://www.codingsoho.com/

分页功能

django 参考文档 https://docs.djangoproject.com/en/1.11/topics/pagination/

后端

class JobEntryListView(ListView):
    model = JobEntry
    filter_class = JobEntryFilter
    def get_context_data(self, *args, **kwargs):
        context = super(JobEntryListView, self).get_context_data(*args, **kwargs)
        ...
        object_list= self.filter_class(self.request.GET, self.get_queryset()).qs
        # pagination
        # remove page related query from GET
        getvars = self.request.GET.copy()
        if 'page' in getvars:
            del getvars['page']
        if 'pagenum_perpage' in getvars:
            del getvars['pagenum_perpage']
        if len(getvars.keys()) > 0:
            context['getvars'] = "&%s" % getvars.urlencode()
        else:
            context['getvars'] = ''
        # set page number per page
        pagenum_perpage = self.request.GET.get('pagenum_perpage', None)
        if pagenum_perpage and pagenum_perpage.isdigit():
            pagenum_perpage = int(pagenum_perpage)
        else:
            pagenum_perpage = 10
        # get record for current page
        paginator = Paginator(object_list, pagenum_perpage)        
        records = None
        page = self.request.GET.get('page')        
        try:
            records = paginator.page(page)
        except PageNotAnInteger:
            # If page is not an integer, deliver first page.
            records = paginator.page(1)
            page = 1
        except EmptyPage:
            # If page is out of range (e.g. 9999), deliver last page of results.
            records = paginator.page(paginator.num_pages)
            page = paginator.num_pages
        # page related value
        page_value = int(page)
        context["start"] = pagenum_perpage*(page_value-1) + 1
        context["end"] = pagenum_perpage * page_value if not page == paginator.num_pages else object_list.count()
        context["total"] = object_list.count()
        context["total_page"] = paginator.num_pages
        display_pages = []
        for page in range(1,6):
            ipage = ((page_value-1)/5)*5 + page
            if not ipage > paginator.num_pages:
                display_pages.append(ipage)
            else:
                break
        context["pages"] = display_pages
        context["page_highlight"] = page_value
        context["page"] = page_value if page_value else page
        context["pagenum_perpage"] = pagenum_perpage
        # new object_list        
        context["object_list"] = records.object_list
        context["records"] = records
        return context

有几个变量先搞清楚

  • 当前页号 page
  • 每页显示条目数 num_perpage
  • 总共页数 total_page
  • 总共记录号 total
  • 当前显示出来可以快捷访问的页 pages
  • 当前显示出来的起始页 start
  • 当前显示出来的最后页 end

实现时,我们首先通过paginator = Paginator(object_list, num_perpage)获取paginator对象,然后通过paginator.page(page)返回记录。

正常情况下,直接将这个返回值传递给模板就可以迭代访问了。但是当前做时一直不行,我就直接返回它的变量records.object_list了。

前端代码

{% load i18n staticfiles %}
<div class="row">
    <div class="col-sm-5">
        <div class="dataTables_info" id="example2_info" role="status" aria-live="polite">{% blocktrans %}Showing {{start}} to {{end}} of {{total}} entries{% endblocktrans %}</div>
        <form  method="GET" action="" style="float: left;">
            <label>{% trans "each page" %}
                <select name="pagenum_perpage" " class="input-sm?">
                    <option value="10" {% if pagenum_perpage == 10 %}selected{% endif %}>10</option>
                    <option value="25" {% if pagenum_perpage == 25 %}selected{% endif %}>25</option>
                    <option value="50" {% if pagenum_perpage == 50 %}selected{% endif %}>50</option>
                    <option value="100" {% if pagenum_perpage == 100 %}selected{% endif %}>100</option>
                </select> {% trans 'entries' %}
            </label>&nbsp;&nbsp;&nbsp;
            <input id="id_des_page" name="page" type="text" value="{{page}}" style="width: 50px; text-align: center;"> / {{total_page}}{% trans 'Page' %}&nbsp;&nbsp;
            <input type='submit' class='btn btn-primary' value="{% trans 'Go!' %}">
        </form>   
    </div>
    <div class="col-sm-7">
        <div class="dataTables_paginate paging_simple_numbers pull-right" id="example2_paginate" >
            <ul class="pagination" style="margin: 0px;">
                <li class="paginate_button previous {% if not object_list.has_previous %}disabled{% endif %}" id="example2_previous">
                    <a href="{% if object_list.has_previous %}?page={{ object_list.previous_page_number }}&pagenum_perpage={{pagenum_perpage}}{% endif %}{{getvars}}" aria-controls="example2" data-dt-idx="0" tabindex="0">{% trans 'previous' %}</a>
                </li>
                {% for page in pages %}
                <li class="paginate_button {% if page == page_highlight %}active{% endif %}">
                    <a href="?page={{page}}&pagenum_perpage={{pagenum_perpage}}{{getvars}}" aria-controls="example2" data-dt-idx="{{forloop.count}}" tabindex="0">{{page}}</a>
                </li>
                {% endfor %}
                <li class="paginate_button next {% if not object_list.has_next %}disabled{% endif %}" id="example2_next">
                    <a href="{% if object_list.has_next %}?page={{ object_list.next_page_number }}&pagenum_perpage={{pagenum_perpage}}{% endif %}{{getvars}}" aria-controls="example2" data-dt-idx="7" tabindex="0">{% trans 'next' %}</a>
                </li>
            </ul>
        </div>
    </div>
</div>    

有几个地方要叙述一下

  • has_previous , has_next 用于判断是否是第一页和最后一页,来决定如何显示“前一页”和“后一页”这两个超链接
  • blocktrans 这儿我们用到了国际化的另一个应用, {% blocktrans %}Showing {{start}} to {{end}} of {{total}} entries{% endblocktrans %}支持字符和数字变量的混写。

混入类 mixin

分页是一个最基本的功能,在每一个类中都写一遍是非常低效的,这儿我们将引入混入类的用法,把分页功能抽象出来

添加包plugin,添加文件mixins.py

将刚才的页面处理函数添加到下面函数

class PageMixin(object):
    def get_context_data(self, *args, **kwargs):

创建新文件pages.html,将刚才的前端代码都放进去。

修改JobEntryListView类,添加基类PageMixin,同时注意在调用super之前将filter之后的object_list传递给它

class JobEntryListView(PageMixin, ListView):
    def get_context_data(self, *args, **kwargs):
        object_list= self.filter_class(self.request.GET, self.get_queryset()).qs
        kwargs.update({'object_list':object_list}) # without this, Paginator will get full object_list for handle
        context = super(JobEntryListView, self).get_context_data(*args, **kwargs)
        # context['object_list'] = self.filter_class(self.request.GET, self.get_queryset()).qs
        ...

这样,分页功能就完成了。

django-pagination库

这个库使用起来非常简单,具体可参考django-pagination

从文件pagination_tags.py看,它有页面的一些相关配置

DEFAULT_PAGINATION = getattr(settings, 'PAGINATION_DEFAULT_PAGINATION', 20)
DEFAULT_WINDOW = getattr(settings, 'PAGINATION_DEFAULT_WINDOW', 4)
DEFAULT_ORPHANS = getattr(settings, 'PAGINATION_DEFAULT_ORPHANS', 0)
INVALID_PAGE_RAISES_404 = getattr(settings,
    'PAGINATION_INVALID_PAGE_RAISES_404', False)

也可以重写模板文件,美化界面。

后面有时间,我打算把上面的模板方法也写成templatetag,这样更加方便在模板中使用。