django之分页

分页

在当前网页如果特别的数据,如果不想当前网页显示太多的内容数据(例如一些文章的目录或者一些小说的章节目录很多地方都用到分页),这时候就需要用分页了。

在Django中数据被分在不同页面中,并带有“上一页/下一页”标签。这些类位于django/core/paginator.py中。

分页器Paginator

Paginator.py源码

import collections
from math import ceil

from django.utils import six
from django.utils.functional import cached_property

class InvalidPage(Exception):
    pass

class PageNotAnInteger(InvalidPage):
    pass

class EmptyPage(InvalidPage):
    pass

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

    def validate_number(self, number):
        """
        Validates the given 1-based page number.
        """
        try:
            number = int(number)
        except (TypeError, ValueError):
            raise PageNotAnInteger(‘That page number is not an integer‘)
        if number < 1:
            raise EmptyPage(‘That page number is less than 1‘)
        if number > self.num_pages:
            if number == 1 and self.allow_empty_first_page:
                pass
            else:
                raise EmptyPage(‘That page contains no results‘)
        return number

    def page(self, number):
        """
        Returns a Page object for the given 1-based page number.
        """
        number = self.validate_number(number)
        bottom = (number - 1) * self.per_page
        top = bottom + self.per_page
        if top + self.orphans >= self.count:
            top = self.count
        return self._get_page(self.object_list[bottom:top], number, self)

    def _get_page(self, *args, **kwargs):
        """
        Returns an instance of a single page.

        This hook can be used by subclasses to use an alternative to the
        standard :cls:`Page` object.
        """
        return Page(*args, **kwargs)

    @cached_property
    def count(self):
        """
        Returns the total number of objects, across all pages.
        """
        try:
            return self.object_list.count()
        except (AttributeError, TypeError):
            # AttributeError if object_list has no count() method.
            # TypeError if object_list.count() requires arguments
            # (i.e. is of type list).
            return len(self.object_list)

    @cached_property
    def num_pages(self):
        """
        Returns the total number of pages.
        """
        if self.count == 0 and not self.allow_empty_first_page:
            return 0
        hits = max(1, self.count - self.orphans)
        return int(ceil(hits / float(self.per_page)))

    @property
    def page_range(self):
        """
        Returns a 1-based range of pages for iterating through within
        a template for loop.
        """
        return six.moves.range(1, self.num_pages + 1)

QuerySetPaginator = Paginator   # For backwards-compatibility.

class Page(collections.Sequence):

    def __init__(self, object_list, number, paginator):
        self.object_list = object_list
        self.number = number
        self.paginator = paginator

    def __repr__(self):
        return ‘<Page %s of %s>‘ % (self.number, self.paginator.num_pages)

    def __len__(self):
        return len(self.object_list)

    def __getitem__(self, index):
        if not isinstance(index, (slice,) + six.integer_types):
            raise TypeError
        # The object_list is converted to a list so that if it was a QuerySet
        # it won‘t be a database hit per __getitem__.
        if not isinstance(self.object_list, list):
            self.object_list = list(self.object_list)
        return self.object_list[index]

    def has_next(self):
        return self.number < self.paginator.num_pages

    def has_previous(self):
        return self.number > 1

    def has_other_pages(self):
        return self.has_previous() or self.has_next()

    def next_page_number(self):
        return self.paginator.validate_number(self.number + 1)

    def previous_page_number(self):
        return self.paginator.validate_number(self.number - 1)

    def start_index(self):
        """
        Returns the 1-based index of the first object on this page,
        relative to total objects in the paginator.
        """
        # Special case, return zero if no items.
        if self.paginator.count == 0:
            return 0
        return (self.paginator.per_page * (self.number - 1)) + 1

    def end_index(self):
        """
        Returns the 1-based index of the last object on this page,
        relative to total objects found (hits).
        """
        # Special case for the last page because there can be orphans.
        if self.number == self.paginator.num_pages:
            return self.paginator.count
        return self.number * self.paginator.per_page

  

使用分页器Paginator

在视图中使用 Paginator来为查询集分页。我们提供视图以及相关的模板来展示如何展示这些结果。

Paginator常用属性

per_page: 每页显示条目数量
count:    数据总个数
num_pages:总页数
page_range:总页数的索引范围,页码的范围,从1开始,例如[1, 2, 3, 4]。

  

Paginator所需参数

object_list   一个列表,元祖或则Django 的Queryset 对象 或则其他对象带有 count() or __len__()的方法

per_page :就是1页显示几条数据

  

Paginator对象的方法

page(number)   :返回在提供的下标处的Page对象,下标以1开始。

  

使用page对象

方法

Page.has_next()
如果有下一页,则返回True。

Page.has_previous()
如果有上一页,返回 True。

Page.has_other_pages()
如果有上一页或下一页,返回True。

Page.next_page_number()
返回下一页的页码。如果下一页不存在,抛出InvalidPage异常。

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对象。

  

django内置分页

代码示例

views.py

  

from django.shortcuts import render
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
# Create your views here.

#模拟测试网页数据
USER_LIST = []
for i in range(1,999):
    temp = {"name":"root"+str(i),"age":i}
    USER_LIST.append(temp)

def listing(request):
    current_page = request.GET.get(‘p‘)
    paginator = Paginator(USER_LIST,10)
    try:
        paginator = paginator.page(current_page)  #获取前端传过来显示当前页的数据
    except PageNotAnInteger:
        # 如果有异常则显示第一页
        paginator = paginator.page(1)
    except EmptyPage:
        # 如果没有得到具体的分页内容的话,则显示最后一页
        paginator = paginator.page(paginator.num_pages)

    return render(request,‘index.html‘,{"users":paginator})

  

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    {% for user in users.object_list %}
        <li>{{ user.name }}-{{ user.age }}</li>
    {% endfor %}
    {% if users.has_previous %}
        <a href="/index?p={{ users.previous_page_number }}">上一页</a>
    {% endif %}
    {% if users.has_next %}
        <a href="/index?p={{ users.next_page_number }}">下一页</a>
    {% endif %}
    <span>{{ users.number }}/{{ users.paginator.num_pages }}</span>
</ul>
</body>
</html>

  

模拟访问测试

http://127.0.0.1:8080/index/?p=123132

http://127.0.0.1:8080/index/?p=阿斯顿发斯蒂芬

  

扩展Django内置分页

上面差不多久这些功能,为了扩张更多的功能我们来扩展Paginator分页器,例如网页页面显示的页码不只是只显示(上一页,下一页)

代码示例

from django.shortcuts import render
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
# Create your views here.

#模拟测试网页数据
USER_LIST = []
for i in range(1,999):
    temp = {"name":"root"+str(i),"age":i}
    USER_LIST.append(temp)

class CustomPaginator(Paginator):
    def __init__(self,current_page,per_pager_num,*args,**kwargs):
        # per_pager_num  显示的页码数量
        self.current_page = int(current_page)
        self.per_pager_num = int(per_pager_num)
        super(CustomPaginator,self).__init__(*args,**kwargs)
    def pager_num_range(self):
        ‘‘‘
        自定义显示页码数
        第一种:总页数小于显示的页码数
        第二种:总页数大于显示页数  根据当前页做判断  a 如果当前页大于显示页一半的时候  ,往右移一下
                                                b 如果当前页小于显示页的一半的时候,显示当前的页码数量
        第三种:当前页大于总页数
        :return:
        ‘‘‘
        if self.num_pages < self.per_pager_num:
            return range(1,self.num_pages+1)

        half_part = int(self.per_pager_num/2)
        if self.current_page <= half_part:
            return range(1,self.per_pager_num+1)

        if (self.current_page+half_part) > self.num_pages:
            return range(self.num_pages-self.per_pager_num+1,self.num_pages)
        return range(self.current_page-half_part,self.current_page+half_part+1)

def listing(request):
    current_page = request.GET.get(‘p‘)
    paginator = CustomPaginator(current_page,11,USER_LIST,10)
    try:
        paginator = paginator.page(current_page)  #获取前端传过来显示当前页的数据
    except PageNotAnInteger:
        # 如果有异常则显示第一页
        paginator = paginator.page(1)
    except EmptyPage:
        # 如果没有得到具体的分页内容的话,则显示最后一页
        paginator = paginator.page(paginator.num_pages)

    return render(request,‘index.html‘,{"users":paginator})

  

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    {% for user in users.object_list %}
        <li>{{ user.name }}-{{ user.age }}</li>
    {% endfor %}

    {% if users.has_previous %}
        <a href="/index?p={{ users.previous_page_number }}">上一页</a>
    {% endif %}

    {% for number in users.paginator.pager_num_range %}
        {% if number == users.number %}
            <a href="/index?p={{ number }}" style="font-size: 33px">{{ number }}</a>
        {% else %}
            <a href="/index?p={{ number }}" >{{ number }}</a>
        {% endif %}

    {% endfor %}

    {% if users.has_next %}
        <a href="/index?p={{ users.next_page_number }}">下一页</a>
    {% endif %}
    <span>{{ users.number }} /{{ users.paginator.num_pages }}</span>
</ul>
</body>
</html>

  

自定制分页器

分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置。

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页...)

3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

4、在数据表中根据起始位置取值,页面上输出数据

需求,需要在页面上显示分页的页面。如:[上一页][1][2][3][4][5][下一页]

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页...)

3、设定显示多少页号

4、获取当前数据总条数

5、根据设定显示多少页号和数据总条数计算出,总页数

6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

7、在数据表中根据起始位置取值,页面上输出数据

8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

代码示例;

1.新建一个分页器的py文件

#!/usr/bin/env python
#-*- coding:utf-8 -*-
from django.utils.safestring import mark_safe

#mark_safe 把字符串转换成html语言
class Pagination(object):
    def __init__(self,totalCount,currentPage,perPageItemNum=10,maxPageNum=7):
        ‘‘‘

        :param totalCount:   数据总个数
        :param currentPage:   当前页
        :param perPageItemNum:  每页显示行数
        :param maxPageNum:  最多显示页面
        ‘‘‘

        self.total_count = totalCount

        try:
            current_page = int(currentPage)
            if current_page <=0:
                current_page = 1
            self.current_page = current_page
        except Exception as e:
            self.current_page = 1

        self.per_page_item_num = perPageItemNum

        self.max_page_num = maxPageNum

    def  start(self):
        ‘‘‘
        自定义一个表达式进行显示几页到几页
        :return:
        ‘‘‘
        return (self.current_page-1)*self.per_page_item_num

    def end(self):
        return self.current_page * self.per_page_item_num

    @property
    def num_pages(self):
        ‘‘‘
        num_pages 属性 总页数
        :return:
        ‘‘‘
        a,b=divmod(self.total_count,self.per_page_item_num)

        if b == 0:
            return a
        return a+1
    def pager_num_range(self):
        ‘‘‘

        :return:
        ‘‘‘

        #总页码小于显示的页码区域
        if self.num_pages < self.max_page_num:
            return range(1,self.max_page_num+1)

        #总页数大于最多显示页  根据当前页做判断  a,b,c

        #a 如果当前页小于显示页一半的时候  ,就只显示当前的页码区域

        part = int(self.max_page_num / 2)
        if self.current_page <= part:
            return range(1, self.max_page_num + 1)

        #b 如果当前页大于显示页一半的时候  ,往右移一下页码
        if (self.current_page + part) > self.num_pages:
            return range(self.num_pages - self.max_page_num + 1, self.num_pages + 1)

        #c:如果当前页大于总页数的
        return range(self.current_page - part, self.current_page + part + 1)

    def page_str(self):

        ‘‘‘
		把自定义后端的一些上一页和下一页的一些标签等等相关内容放到前端去显示

        :return:
        ‘‘‘

        page_list = []
        first = "<li><a href=‘/index2?p=1‘>首页</a></li>"
        page_list.append(first)

        if self.current_page == 1:
            prev = "<li><a href=‘#‘>上一页</a></li>"
        else:
            prev = "<li><a href=‘/index2?p=%s‘>上一页</a></li>" % (self.current_page - 1,)
        page_list.append(prev)
        for i in self.pager_num_range():
            if i == self.current_page:
                temp = "<li class=‘active‘><a href=‘/index2?p=%s‘>%s</a></li>" % (i, i)
            else:
                temp = "<li><a href=‘/index2?p=%s‘>%s</a></li>" % (i, i)
            page_list.append(temp)

        if self.current_page == self.num_pages:
            nex = "<li><a href=‘#‘>下一页</a></li>"
        else:
            nex = "<li><a href=‘/index2?p=%s‘>下一页</a></li>" % (self.current_page + 1,)
        page_list.append(nex)

        last = "<li><a href=‘/index2?p=%s‘>尾页</a></li>" % (self.num_pages,)
        page_list.append(last)
        res = ‘‘.join(page_list)

        return mark_safe(res)

  

2.url路由

from django.conf.urls import url
from django.contrib import admin
from  app01 import views
urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^index/$‘, views.listing),
    url(r‘^index2/$‘, views.index2),
]

  

3.视图

from django.shortcuts import render
from app01.pager import Pagination

#模拟测试网页数据
USER_LIST = []
for i in range(1,999):
    temp = {"name":"root"+str(i),"age":i}
    USER_LIST.append(temp)
def index2(request):
    current_page = request.GET.get(‘p‘)
    page_obj = Pagination(len(USER_LIST), current_page)

    data_list = USER_LIST[page_obj.start():page_obj.end()]
    return render(request, ‘index2.html‘, {‘data‘: data_list, ‘page_obj‘: page_obj})

  

3.模板的html文件index2.html的内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for row in data %}
            <li>{{ row.name }}-{{ row.age }}</li>
        {% endfor %}
    </ul>
    {% for i in page_obj.pager_num_range %}
        页码显示部分区域
        <a href="/index2?p={{ i }}">{{ i }}</a>
    {% endfor %}
    <hr>
    <ul >{{ page_obj.page_str }}</ul>
</body>
</html>

  

4.访问测试

http://127.0.0.1:9999/index2/?p=4 

注:是用的本机做测试

  

总结:

总结,分页时需要做三件事:

  • 创建处理分页数据的类
  • 根据分页数据获取数据
  • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
时间: 2024-10-28 00:52:46

django之分页的相关文章

django 自定义分页模块

django 自定义分页模块 from django.shortcuts import render, HttpResponse, redirect from django.utils.safestring import mark_safe class Page(object): def __init__(self, current_page): self.current_page = int(current_page) @property def start(self): return (se

django orm 分页(paginator)取数据出现警告manage.py:1: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: &lt;class &#39;sign.models.Guest&#39;&gt; QuerySet.

使用django的orm做分页(Paginator)时出现了下面的警告 In [19]: p=Paginator(guest_list,2) manage.py:1: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'sign.models.Guest'> QuerySet. #!/usr/bin/env python 网上搜的

Django html 分页

16.分页 django 自带的分页:django paginator 参考:https://docs.djangoproject.com/en/1.10/topics/pagination/ >>> from django.core.paginator import Paginator >>> objects = ['john', 'paul', 'george', 'ringo'] >>> p = Paginator(objects, 2) >

Django - Ajax分页

目前总结了2种方法: 1. Ajax 分页 尼玛各种google,stackoverflow,搞了好久才总结出这个,之前使用Pagination tag loading的方式不好用,并且不能进行ajax提交请求的页面无刷新的方式去分页 1.view.py 1 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger 2 from django.shortcuts import render 3 def xxx

django的分页--不全也未实现

一.Django内置分页 Paginator 二.自定义分页 分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置. 1.设定每页显示数据条数 2.用户输入页码(第一页.第二页...) 3.根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置 4.在数据表中根据起始位置取值,页面上输出数据 需求又来了,需要在页面上显示分页的页面.如:[上一页][1][2][3][4][5][下一页] 1.设定每页显示数据条数 2.用户输入页码(第一页.第二

django之分页插件

1 from django.utils.safestring import mark_safe 2 3 4 class Page: 5 def __init__(self, current_page, data_count, per_page_count=2, pager_num=7): 6 self.current_page = current_page 7 self.data_count = data_count 8 self.per_page_count = per_page_count

django 实现分页功能

分页效果: 视图代码: 1 # -*- coding: utf-8 -*- 2 from django.shortcuts import render,get_object_or_404 3 from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage 4 5 from .models import Article 6 7 # Create your views here. 8 9 def index(request

django之分页、cookie装饰器

一.分页代码如下 from django.utils.safestring import mark_safe class Page: def __init__(self, current_page, data_count, per_page_count=10, pager_num=7): self.current_page = current_page self.data_count = data_count self.per_page_count = per_page_count self.p

Django自定义分页、bottle

一.使用django实现之定义分页 1.自定义分页在django模板语言中,通过a标签实现; 2.前段a标签使用<a href="/user_list/?page=1">1</a>,将page的值传送到函数/user_list/中,后端在user_list中通过request.GET.get('page',1)获取当前页; 3.从数据库中获取特定行的数据,使用result = models.UserList.objects.all()[start:end]获取,