自定义分页

1、目的&环境准备

目的把分页写成一个模块的方式然后在需要分页的地方直接调用模块就行了。

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

配置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'^user_list/',views.user_list),
]

注册APP

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

配置models

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()

2、分析

分页在基本上行所有的大型网站中都是需要的,比如博客园的分页,当我们查询的时候或查看的时候他有很多博文,难道他是一下把所有的博文都给我们返回的吗?当然不是:如下图

我们可以根据提示来点击上一页和下一页或者点击我们想要查看的页面,然后显示我们要查看的博文链接!而不是一下把所有的博文给显示出来,这样即节省流量又能改善用户体验。

3、逐步完善分页代码

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

3.1、配置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.2、配置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.3、取指定的条数

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

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

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

3.4、每页显示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})

2、生成按钮页

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

先看下如果咱们手动写的话!

<!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

 

Python之路【第十九篇】自定义分页实现(模块化)的更多相关文章

  1. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  2. Python之路(第十九篇)hashlib模块

    一.hashlib模块 HASH Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值 ...

  3. Python之路(第十二篇)程序解耦、模块介绍\导入\安装、包

    一.程序解耦 解耦总的一句话来说,减少依赖,抽象业务和逻辑,让各个功能实现独立. 直观理解“解耦”,就是我可以替换某个模块,对原来系统的功能不造成影响.是两个东西原来互相影响,现在让他们独立发展:核心 ...

  4. Python开发【第十九篇】:Python操作MySQL

    本篇对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb ...

  5. Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类

    一.类的装饰器 类作为一个对象,也可以被装饰. 例子 def wrap(obj): print("装饰器-----") obj.x = 1 obj.y = 3 obj.z = 5 ...

  6. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

  7. Python之路(第二十六篇) 面向对象进阶:内置方法

    一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__g ...

  8. Python之路(第二十五篇) 面向对象初级:反射、内置方法

    [TOC] 一.反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它 ...

  9. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

  10. Python之路(第十八篇)shutil 模块、zipfile模块、configparser模块

    一.shutil 模块 1.shutil.copyfileobj(fsrc, fdst[, length]) 将文件内容拷贝到另一个文件中,需要打开文件 import shutil shutil.co ...

随机推荐

  1. android setDestinationInExternalPublicDir 下载到SD卡根目录

    一:setDestinationInExternalPublicDir(“Trinea”, “MeiLiShuo.apk”);表示设置下载地址为sd卡的Trinea文件夹,文件名为MeiLiShuo. ...

  2. android 学习中的一些问题记录 主要是概念问题

    一些问题记录 应用程序 res 目录常见的目录有哪些,分别放置什么类型的资源? animator/ 和anim/ 放的都是定义动画的XML文件,两个地方的动画类型不同. color/ XML文件:定义 ...

  3. 理解Docker(2):Docker 镜像

    本系列文章将介绍Docker的有关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...

  4. PhotoShop算法原理解析系列 - 像素化---》碎片。

    接着上一篇文章的热度,继续讲讲一些稍微简单的算法吧. 本文来讲讲碎片算法,先贴几个效果图吧:             这是个破坏性的滤镜,拿美女来说事是因为搞图像的人90%是男人,色色的男人. 关于碎 ...

  5. bzoj-4514(网络流)

    题目链接: 4514: [Sdoi2016]数字配对 Description 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数 ...

  6. Bean生命周期及BeanFactory

    1.spring通过BeanFactory灵活配置.管理bean,Spring对管理的bean没有任何特别的要求,完全支持对POJO的管理: 2.BeanFactory有个ApplicationCon ...

  7. python高级之操作数据库

    python高级之操作数据库 本节内容 pymysql介绍及安装 使用pymysql执行sql 获取新建数据自增ID fetch数据类型设置 1.pymysql介绍及安装 在python2中连接数据库 ...

  8. Threejs 物体闪烁

    摸索经验告诉我,对物体的材料关闭深度检测有奇效. 即: depthTest: false 有同类问题的童鞋可以试试.

  9. bzoj 3163: [Heoi2013]Eden的新背包问题

    Description "寄没有地址的信,这样的情绪有种距离,你放着谁的歌曲,是怎样的心心静,能不能说给我听."失忆的Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的 ...

  10. [译]Spring构建微服务

    此文为译文,原文地址 介绍 本文通过一个使用Spring.Spring Boot和Spring Cloud的小例子来说明如何构建微服务系统. 我们可以通过数个微服务组合成一个大型系统. 我们可以想象下 ...