自定義分頁
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>

#!/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))