Python之路【第十九篇】自定义分页实现(模块化)

自定义分页

一、目的&环境准备javascript

目的把分页写成一个模块的方式而后在须要分页的地方直接调用模块就好了。html

环境准备Django中生成一个APP而且注册,配置URL&Views前端

配置URLjava

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^user_list/',views.user_list),
]

注册APPpython

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]

配置modelsweb

from __future__ import unicode_literals

from django.db import models

# Create your models here.

class UserList(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()

二、分析数据库

分页在基本上行全部的大型网站中都是须要的,好比博客园的分页,当咱们查询的时候或查看的时候他有不少博文,难道他是一下把全部的博文都给咱们返回的吗?固然不是:以下图django

咱们能够根据提示来点击上一页和下一页或者点击咱们想要查看的页面,而后显示咱们要查看的博文连接!而不是一下把全部的博文给显示出来,这样即节省流量又能改善用户体验。后端

三、逐步完善分页代码浏览器

首先我们先建立500条数据(以前不要忘记生成数据库),在第一访问时设置下就OK

3.一、配置views建立数据

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.

def user_list(request):
    for i in range(500):
        dic = {'username': 'name_%d' % i, 'age': i}
        models.UserList.objects.create(**dic)
    return HttpResponse('OK')

3.二、配置html显示数据并经过views获取数据显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        {% for line in result %}
            <tr>
                <td>{{ line.username }}</td>
                <td>{{ line.age }}</td>
            </tr>
        {% endfor %}
    </table>

    <div>
        {{ pager_str|safe }}
    </div>
</body>
</html>

views

def user_list(request):
    result = models.UserList.objects.all()
    return render(request,'user_list.html',{'result':result})

这样,前端能够正常显示我们去的数据了,可是一会儿把全部的数据都取出来了。不是我们想要的结果。

3.三、取指定的条数

好比我想取前10条怎么取呢?

def user_list(request):
    result = models.UserList.objects.all()[0:10]
    return render(request,'user_list.html',{'result':result})

那我让[0:10]动态起来就能够了!开始和结尾。

3.四、每页显示10条数据

向用户获取页数,咱们能够配置URL在URL中有两种方式查询直接在URL中配置正则,而后在views里能够经过参数来获取用户传过来的值,

也能够经过?a=9 &b=10 这样在Veiws函数里能够经过request.GET['a']取值了,而且能够设置默认值request.GET.get('a',0)

def user_list(request):
    current_page = request.GET.get('page')
    print current_page
    result = models.UserList.objects.all()[0:10]
    return render(request,'user_list.html',{'result':result})

测试:

   这里的问号是经过get的方式向后端发送数据,咱们也能够修改form表单中method改成GET,那么数据访问的时候就是经过GET方式向后端提交数据的,当点击form表单提交数据的时候,form里的内容会自动添加到浏览器的URL中以上面的格式!

虽然POST和GET均可以向后端提交数据可是规定:GET通常用来查询使用,POST通常用来提交修改数据使用,根据实际的状况来定!

看下面的代码:

def user_list(request):
    current_page = request.GET.get('page',1)
    print current_page
    start = 0 #10 20 (current_page-1)*10
    end = 10  #20 30  current_page*10
    result = models.UserList.objects.all()[start:end]
    return render(request,'user_list.html',{'result':result})

咱们如今让start & end 动态起来页面动态起来是否是就更好了?

def user_list(request):
    current_page = request.GET.get('page',1)
    current_page = int(current_page)
    start = (current_page-1)*10 #10 20 (current_page-1)*10
    end = current_page*10  #20 30  current_page*10
    result = models.UserList.objects.all()[start:end]
    return render(request,'user_list.html',{'result':result})

而后打开网页测试下,修改page的值来看下显示效果:

http://127.0.0.1:8000/user_list/?page=2

http://127.0.0.1:8000/user_list/?page=3

http://127.0.0.1:8000/user_list/?page=4  

分页代码整理

咱们经过把上面的代码整理一下整理到函数或类里下次使用的时候直接调用类或者函数便可。

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.


class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10


def user_list(request):
    current_page = request.GET.get('page',1)
    page_obj = Pager(current_page)
    #把方法改形成属性(2),这样在下面调用方法的时候就不须要加括号了
    result = models.UserList.objects.all()[page_obj.start:page_obj.end]
    return render(request,'user_list.html',{'result':result})

二、生成按钮页

上面的功能实现了分页如今,咱们是否是能够在下面生成一堆的按钮来让用户点击,而不是在浏览器中输入!

先看下若是我们手动写的话!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        {% for line in result %}
            <tr>
                <td>{{ line.username }}</td>
                <td>{{ line.age }}</td>
            </tr>
        {% endfor %}
    </table>

    <div>
        <a href="/user_list/?page=1">1</a>
        <a href="/user_list/?page=2">2</a>
        <a href="/user_list/?page=3">3</a>
        <a href="/user_list/?page=4">4</a>
        <a href="/user_list/?page=5">5</a>
    </div>
</body>
</html>

以下图:

 

目的是为了实现左侧的效果,可是我们不可能手写吧?因此应该在后台建立,这个是否是就是总共有多少页?

需求分析:

每页显示10条数据

共500条数据就是50页,那有501条数据呢?是多少页是51页。

能够经过divmod(500,10),他会的到一个元组,第一个值是值,第二个值是余数,咱们就经过判断他是否有余数来判断是不是整页,若是有余数在整页基础加1便可!

咱们获取了全部的页数以后能够吧他返回在前端生成,可是这样咱们后端作了分页以后前端是否是也作了分页操做?

咱们能够在后端生成一个大字符串而后把他返回给前端(拼接的URL)

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.


class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10


def user_list(request):
    current_page = request.GET.get('page',1)
    page_obj = Pager(current_page)
    #吧方法未形成属性(2),这样在下面调用方法的时候就不须要加括号了
    result = models.UserList.objects.all()[page_obj.start:page_obj.end]

    all_item = models.UserList.objects.all().count()
    all_page,div = divmod(all_item,10)

    if div > 0:
        all_page +=1

    pager_str = ""
    for i in range(1,all_page+1):
        #每次循环生成一个标签
        temp = '<a href="/user_list/?page=%d">%d</a>' %(i,i,)
        #把标签拼接而后返回给前端
        pager_str += temp

    return render(request,'user_list.html',{'result':result,'pager_str':pager_str})

前端配置:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        {% for line in result %}
            <tr>
                <td>{{ line.username }}</td>
                <td>{{ line.age }}</td>
            </tr>
        {% endfor %}
    </table>

    <div>
        {{ pager_str }}
    </div>
</body>
</html>

可是有个问题以下图:

这个是什么问题致使的呢?

我们在以前的文章学过一个CSRF跨站请求伪造的安全设置,这里还有一个值得说的安全就是XSS攻击:

XSS攻击:跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。

XSS是一种常常出如今web应用中的计算机安全漏洞,它容许恶意web用户将代码植入到提供给其它用户使用的页面中。好比这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞因为被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。

我们上面看到的内容就是Django为了防止这样的状况产生而设置的机制,例子以下:

若是,写的简单的script能够被保存为HTML标签的话那么?只要有人打开这个页面都会提示一个信息,这样是否是不安全,这个是小的说的!

若是说这里是一个获取Cookie而后发送到指定服务器而后服务器能够经过cookie访问你的我的信息是否是就可怕了?!

 那么怎么解决这个问题呢?能够经过模板语言的一个函数来告诉Django这个是安全的:加一个safe (在文章的后面还有一种方法在后端标记他是安全的)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        {% for line in result %}
            <tr>
                <td>{{ line.username }}</td>
                <td>{{ line.age }}</td>
            </tr>
        {% endfor %}
    </table>

    <div>
        {{ pager_str|safe }}
    </div>
</body>
</html>

效果以下:

这样全显示出来了,不是很好,最好显示11页而后,当用户点击第6页的时候,这个6永远在中间!因此咱们也要让页码动态起来!

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.


class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10

    def page_str(self,all_item,base_url):
        all_page, div = divmod(all_item, 10)

        if div > 0:
            all_page += 1

        pager_str = ""

        #默承认以看到的页码11个

        start = self.current_page - 5
        end = self.current_page + 6

        #把页面动态起来传入起始和结束
        for i in range(start, end):

            #判断是否为当前页
            if i == self.current_page:
                temp = '<a style="color:red;font-size:26px;padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
            else:
                temp = '<a style="padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)

            # 把标签拼接而后返回给前端
            pager_str += temp
        return pager_str

def user_list(request):
    current_page = request.GET.get('page',1)
    page_obj = Pager(current_page)
    #吧方法未形成属性(2),这样在下面调用方法的时候就不须要加括号了
    result = models.UserList.objects.all()[page_obj.start:page_obj.end]
    all_item = models.UserList.objects.all().count()
    pager_str = page_obj.page_str(all_item,'/user_list/')

    return render(request,'user_list.html',{'result':result,'pager_str':pager_str})

前端页面不用修改了,看效果以下:

如今仍是有问题的,当你如今点击1,2,3,4,5的时候由于前面没有页面就会出现负值!

而且最后一页页会出现无穷大,可是没有数据,那怎么解决呢?

'''
若是总页数 <= 11 好比9页:
  那么,还有必要让页码动起来吗?就不必了直接显示总共页数就好了!start就是1,end就是9就好了
else:
  若是当前页小宇6:
    start:1
    end:11
  若是当前页大于6:
    start:当前页-5
    end:当前页+6
    若是当前页+6 >总页数:
      start:总页数-10
      end:总页数
'''

修改后的代码:

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.


class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10

    def page_str(self,all_item,base_url):
        all_page, div = divmod(all_item, 10)

        if div > 0:
            all_page += 1

        pager_str = ""

        # #默承认以看到的页码11个
        #
        # start = self.current_page - 5
        # end = self.current_page + 6
        #
        # #把页面动态起来传入起始和结束
        # for i in range(start, end):
        #
        #     #判断是否为当前页
        #     if i == self.current_page:
        #         temp = '<a style="color:red;font-size:26px;padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
        #     else:
        #         temp = '<a style="padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
        #
        #     # 把标签拼接而后返回给前端
        #     pager_str += temp

        if all_page <= 11:
            start = 1
            end = all_page
        else:
            if self.current_page <= 6:
                start = 1
                end = 11 + 1
            else:
                start = self.current_page - 5
                end = self.current_page + 6
                if self.current_page + 6 > all_page:
                    start = all_page - 10
                    end = all_page + 1

        #把页面动态起来传入起始和结束
        for i in range(start, end):

            #判断是否为当前页
            if i == self.current_page:
                temp = '<a style="color:red;font-size:26px;padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
            else:
                temp = '<a style="padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)

            # 把标签拼接而后返回给前端
            pager_str += temp


        return pager_str

def user_list(request):
    current_page = request.GET.get('page',1)
    page_obj = Pager(current_page)
    #把方法改形成属性(2),这样在下面调用方法的时候就不须要加括号了
    result = models.UserList.objects.all()[page_obj.start:page_obj.end]
    all_item = models.UserList.objects.all().count()
    pager_str = page_obj.page_str(all_item,'/user_list/')

    return render(request,'user_list.html',{'result':result,'pager_str':pager_str})

这样简单分页就完成了,如今咱们能够在给他增长一个“上一页” & 下一页

咱们能够把他给为一个列表,而后每次生成的标签append到列表中,而后分别在列表中最前面和后面增长,上一页和下一页

#/usr/bin/env python
#-*- coding:utf-8 -*-
from django.shortcuts import render
from django.shortcuts import HttpResponse
import models
# Create your views here.


class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10

    def page_str(self,all_item,base_url):
        all_page, div = divmod(all_item, 10)

        if div > 0:
            all_page += 1

        pager_list = []

        if all_page <= 11:
            start = 1
            end = all_page
        else:
            if self.current_page <= 6:
                start = 1
                end = 11 + 1
            else:
                start = self.current_page - 5
                end = self.current_page + 6
                if self.current_page + 6 > all_page:
                    start = all_page - 10
                    end = all_page + 1

        #把页面动态起来传入起始和结束
        for i in range(start, end):

            #判断是否为当前页
            if i == self.current_page:
                temp = '<a style="color:red;font-size:26px;padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
            else:
                temp = '<a style="padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)

            # 把标签拼接而后返回给前端
            pager_list.append(temp)

        #上一页
        if self.current_page > 1:
            pre_page = '<a href="%s?page=%d">上一页</a>' % (base_url, self.current_page - 1)
        else:
            # javascript:void(0) 什么都不干
            pre_page = '<a href="javascript:void(0);">上一页</a>'
        #下一页
        if self.current_page >= all_page:
            next_page = '<a href="javascript:void(0);">下一页</a>'
        else:
            next_page = '<a href="%s?page=%d">下一页</a>' % (base_url, self.current_page + 1)

        pager_list.insert(0, pre_page)
        pager_list.append(next_page)

        return "".join(pager_list)

def user_list(request):
    current_page = request.GET.get('page',1)
    page_obj = Pager(current_page)
    #把方法改形成属性(2),这样在下面调用方法的时候就不须要加括号了
    result = models.UserList.objects.all()[page_obj.start:page_obj.end]
    all_item = models.UserList.objects.all().count()
    pager_str = page_obj.page_str(all_item,'/user_list/')

    return render(request,'user_list.html',{'result':result,'pager_str':pager_str})

最后写成模块,也能够吧分页的数量动态化!

 

前面说过关于XSS的问题,还有一种方式在后端告诉他是安全的便可,前端就不须要标记了。

导入并使用模块便可:

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

#把拼接的HTML标记为安全的便可
return mark_safe("".join(pager_list))
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        {% for line in result %}
            <tr>
                <td>{{ line.username }}</td>
                <td>{{ line.age }}</td>
            </tr>
        {% endfor %}
    </table>

    <div>
        {{ pager_str }}
    </div>
</body>
</html>
html
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from django.utils.safestring import mark_safe

class Pager(object):
    def __init__(self,current_page):
        self.current_page = int(current_page)
    #把方法伪形成属性(1)
    @property
    def start(self):
        return (self.current_page-1)*10
    @property
    def end(self):
        return self.current_page*10

    def page_str(self,all_item,base_url):
        all_page, div = divmod(all_item, 10)

        if div > 0:
            all_page += 1

        pager_list = []

        if all_page <= 11:
            start = 1
            end = all_page
        else:
            if self.current_page <= 6:
                start = 1
                end = 11 + 1
            else:
                start = self.current_page - 5
                end = self.current_page + 6
                if self.current_page + 6 > all_page:
                    start = all_page - 10
                    end = all_page + 1

        #把页面动态起来传入起始和结束
        for i in range(start, end):

            #判断是否为当前页
            if i == self.current_page:
                temp = '<a style="color:red;font-size:26px;padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)
            else:
                temp = '<a style="padding: 5px" href="%s?page=%d">%d</a>' % (base_url,i,i)

            # 把标签拼接而后返回给前端
            pager_list.append(temp)

        #上一页
        if self.current_page > 1:
            pre_page = '<a href="%s?page=%d">上一页</a>' % (base_url, self.current_page - 1)
        else:
            # javascript:void(0) 什么都不干
            pre_page = '<a href="javascript:void(0);">上一页</a>'
        #下一页
        if self.current_page >= all_page:
            next_page = '<a href="javascript:void(0);">下一页</a>'
        else:
            next_page = '<a href="%s?page=%d">下一页</a>' % (base_url, self.current_page + 1)

        pager_list.insert(0, pre_page)
        pager_list.append(next_page)

        return mark_safe("".join(pager_list))
paging
相关文章
相关标签/搜索