django:分页
一,什么是分頁
網(wǎng)頁是用戶與網(wǎng)站進(jìn)行交互的主要場所,這種交互主要指數(shù)據(jù)收集與數(shù)據(jù)展示。
從試想一下,為什么我們幾乎不在網(wǎng)頁中一次性展示請求獲得的所有數(shù)據(jù)呢?
如果這個數(shù)據(jù)量相當(dāng)小,比如只有幾十條,那么一般情況下無需擔(dān)心,一股腦渲染到頁面中就行。
但如果這個數(shù)據(jù)量比較大,比如幾百幾千幾萬條,且一旦這種操作比較頻繁,顯然就會增加服務(wù)器負(fù)載,主要瓶頸是數(shù)據(jù)庫。
這里不談如何實現(xiàn)高并發(fā),只談如何以輕量化的方式獲取并展示數(shù)據(jù)。
一種有效的方式就是實現(xiàn)分頁查詢:將一次性獲取所有數(shù)據(jù)的操作,分解為多次查詢操作,每次操作只查詢并展示一部分?jǐn)?shù)據(jù)。其中每一次查詢就是獲取一頁數(shù)據(jù)。
效果如下:
具體來說有三種方式實現(xiàn):
二,django的分頁組件
django的分頁組件可以將數(shù)據(jù)被分割在幾個頁面上,并帶有“上一頁/下一頁”鏈接。
(一)Paginator 類和 Page 類
Paginator 類是分頁組件的核心,它完成將 QuerySet 拆分為 Page 對象的所有繁重工作。
>>> from django.core.paginator import Paginator# objects 是一個列表、元組、QuerySet 或其他具有 count() 或 __len__() 方法的可切片對象。 # 如果是 QuerySet,則應(yīng)該是有序的,例如使用 order_by() 子句或使用模型上的默認(rèn) ordering。 >>> objects = ['john', 'paul', 'george', 'ringo']# 獲取分頁器實例 >>> p = Paginator(objects, 2) # 可迭代對象,每頁數(shù)量# 獲取所有頁面中的對象總數(shù) >>> p.count 4# 獲取總頁數(shù) >>> p.num_pages 2# 以 1 為基礎(chǔ)的頁碼范圍迭代器 >>> type(p.page_range) <class 'range_iterator'> >>> p.page_range range(1, 3)# 獲取指定頁 >>> page1 = p.page(1) >>> page1 <Page 1 of 2> # 獲取指定頁中的內(nèi)容 >>> page1.object_list ['john', 'paul'] >>> page2 = p.page(2) >>> page2.object_list ['george', 'ringo']# 判斷當(dāng)前頁是否有下一頁 >>> page2.has_next() False # 判斷當(dāng)前頁是否有上一頁 >>> page2.has_previous() True >>> page2.has_other_pages() True# 當(dāng)前頁的下一頁頁碼 >>> page2.next_page_number() Traceback (most recent call last): ... EmptyPage: That page contains no results # 當(dāng)前頁的上一頁頁碼 >>> page2.previous_page_number() 1# 獲取當(dāng)前頁第一條數(shù)據(jù)在所有數(shù)據(jù)中的索引 >>> page2.start_index() # The 1-based index of the first item on this page 3 # 獲取當(dāng)前頁末一條數(shù)據(jù)在所有數(shù)據(jù)中的索引 >>> page2.end_index() # The 1-based index of the last item on this page 4# 獲取錯誤的頁碼將報錯 >>> p.page(0) Traceback (most recent call last): ... EmptyPage: That page number is less than 1 >>> p.page(3) Traceback (most recent call last): ... EmptyPage: That page contains no results(二)在函數(shù)視圖中使用分頁組件
# FBV from django.core.paginator import Paginator from django.shortcuts import renderfrom myapp.models import Contactdef listing(request):contact_list = Contact.objects.all()paginator = Paginator(contact_list, 25) # Show 25 contacts per page.page_number = request.GET.get('page')page_obj = paginator.get_page(page_number)return render(request, 'list.html', {'page_obj': page_obj}) # template {% for contact in page_obj %}{# Each "contact" is a Contact model object. #}{{ contact.full_name|upper }}<br>... {% endfor %}<div class="pagination"><span class="step-links">{% if page_obj.has_previous %}<a href="?page=1">« first</a><a href="?page={{ page_obj.previous_page_number }}">previous</a>{% endif %}<span class="current">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.</span>{% if page_obj.has_next %}<a href="?page={{ page_obj.next_page_number }}">next</a><a href="?page={{ page_obj.paginator.num_pages }}">last »</a>{% endif %}</span> </div>在URL中攜帶page參數(shù)。
(三)在類視圖中使用分頁組件
# CBV class StudentList(ListView):model = StudentInfopaginate_by = 25template_name = 'list.html'CBV 會在指定了 paginate_by 屬性后
(四)對分頁的改進(jìn)
以上兩種方法夠簡單,但正因如此,它倆都有一個缺點:會一股腦兒地將頁碼全部顯示出來。
比如:
顯然不夠友好,要是頁碼有切割就好了:
最簡單的解決方法就是使用第三方的分頁插件一勞永逸。
(五)使用分頁插件Django-pure-pagination
Django-pure-pagination是比較出名的分頁插件。
1,安裝并注冊:
pip install django-pure-pagination INSTALLED_APPS = (...'pure_pagination', )2,配置分頁切割方式:
3,函數(shù)視圖:
from pure_pagination import Paginator, PageNotAnIntegerdef test_dpp(request):school_list = SchoolInfo.objects.all()paginator = Paginator(school_list, 5, request=request)try:page_number = request.GET.get('page', 1)except PageNotAnInteger:page_number = 1page_obj = paginator.page(page_number)return render(request, 'list.html', {'page_obj': page_obj})4,模板:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><style type="text/css">a {padding: 5px 10px;color: white;background-color: #2caa0d;margin: 1px; /*設(shè)置標(biāo)簽 a 之間的間隔*/text-decoration: none; /*去除頁碼數(shù)字下面的下劃線*/}a:hover {color: black;}.current {color: black;}</style> </head> <body><div>{% for school in page_obj.object_list %}<ul><li>{{ school.name }}<br></li></ul>{% endfor %} </div>{#Django-pure-pagination基礎(chǔ)渲染方法#} {#<div id="pagination">#} {# {{ page_obj.render }}#} {#</div>#}<div class="pageturn">{% if page_obj.has_previous %}<a href="?{{ page_obj.previous_page_number.querystring }}">上一頁</a>{% endif %}{% for page in page_obj.pages %}{% if page %}{% ifequal page page_obj.number %}<a href="?{{ page.querystring }}"><span class="page-link current">{{ page }}</span></a>{% else %}<a href="?{{ page.querystring }}" class="page">{{ page }}</a>{% endifequal %}{% else %}<a href="">...</a>{% endif %}{% endfor %}{% if page_obj.has_next %}<a href="?{{ page_obj.next_page_number.querystring }}">下一頁</a>{% endif %} </div> </body> </html>5,路由:
from pagination.views import test_dppurlpatterns = [path('admin/', admin.site.urls),path('', test_dpp), ]效果如下:
實際上,django project的分頁應(yīng)該是參考過這個插件的實現(xiàn)方式。
三,AJAX 與分頁
(一)AJAX 簡介
AJAX 就是用來描述一種使用現(xiàn)有技術(shù)(HTML 或 XHTM、CSS、JavaScript、DOM、XML、XSLT以及最重要的 XMLHttpRequest)集合的異步處理方法。
當(dāng)使用結(jié)合了這些技術(shù)的 AJAX 模型以后,網(wǎng)頁應(yīng)用能夠快速地將增量更新呈現(xiàn)在用戶界面上,而不需要重載(刷新)整個頁面。這使得程序能夠更快地回應(yīng)用戶的操作。
1,原生JavaScript實現(xiàn) AJAX
原生 JavaScript 實現(xiàn) AJAX 請求的過程比較復(fù)雜,簡單來說就分為以下幾步:
- 定義 XMLHttpRequest對象。
- 在 onreadystatechange 方法中注冊事件處理函數(shù),準(zhǔn)備接收響應(yīng)數(shù)據(jù),并進(jìn)行處理。
- 調(diào)用 XMLHtφRequest 對象的 open() 方法訪問服務(wù)器端 URL 地址。
- 調(diào)用 XMLHttpRequest 對象的 send() 方法發(fā)送請求。
舉個例子🌰:
<!DOCTYPE html> <html lang="en"> <head><meta charset="utf-8"><title>AJAX</title><script>function loadXMLDoc() {let xmlhttp;if (window.XMLHttpRequest) {// IE7+, Firefox, Chrome, Opera, Safari 瀏覽器執(zhí)行代碼xmlhttp = new XMLHttpRequest();} else {// IE6, IE5 瀏覽器執(zhí)行代碼xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.onreadystatechange = function () {if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {//取到的內(nèi)容:一個JSON對象。相當(dāng)于一個python的列表,列表里面是嵌套的字典//[{"model": "cmdb.article", "pk": 1, "fields": {"title": "\u7b2c\u4e00\u4e2a\u6807\u9898", "content": "\u7b2c\u4e00\u6761\u5185\u5bb9\uff01\uff01\uff01"}}]const data = JSON.parse(xmlhttp.responseText);const element = document.getElementById("myDiv");element.innerText = JSON.stringify(data, null, "\t");display(element, data);}}xmlhttp.open("GET", "/thejson/", true);xmlhttp.send();alert(xmlhttp.getAllResponseHeaders());}function display(element, data) {const children = element.childNodes;for (var i = 0; i < children.length; i++) {if (children[i].id === "content")element.removeChild(children);}const len = eval(data).length;for (var i = 0; i < len; i++) {let newP = document.createElement("p");newP.setAttribute("id", "content");var content = data[i].pk + " " + data[i].fields.title + " " + data[i].fields.contentlet newContent = document.createTextNode(content);newP.appendChild(newContent);element.appendChild(newP);}}</script> </head> <body><div id="myDiv"><h2>使用 AJAX 獲得文本內(nèi)容</h2></div> <button type="button" onclick="loadXMLDoc()">獲得內(nèi)容</button></body> </html>還能進(jìn)一步設(shè)置請求頭以實現(xiàn)內(nèi)容協(xié)商。
2,jQuery實現(xiàn) AJAX
jQuery 封裝了常用的、復(fù)雜的一些 JavaScript 操作。
除了可以使用全局性函數(shù)load()、get()、post()實現(xiàn)頁面的異步調(diào)用和與服務(wù)器交互數(shù)據(jù)外,在 jQuery 還有一個功能更為強(qiáng)悍的最底層的方法$.ajax(),該方法不僅可以方便地實現(xiàn)上述三個全局函數(shù)完成的功能,還更多地關(guān)注實現(xiàn)過程中的細(xì)節(jié)。
舉個例子🌰:
$.ajax({type: "POST",url: "some.php",data: "name=John&location=Boston",success: function(msg){alert( "Data Saved: " + msg );} });(二)在django中使用 AJAX
一個簡單的參考,可見使用AJAX與Django通信。
(三)使用 AJAX 實現(xiàn)頁面的滾動加載
AJAX需要一個觸發(fā)條件,所以我們通常將它綁定到一個 DOM 事件上。
我們可以將將“下一頁”等操作的觸發(fā)綁定到 DOM 的點擊事件上,配合 Ajax 實現(xiàn)實現(xiàn)頁面的局部刷新。
也能只將“下一頁”操作的觸發(fā)綁定到頁面滾事件,當(dāng)滾動到末尾時加載下一頁,就能實現(xiàn) infinite scroll 或者 endless 效果,比如在 reddit 首頁不斷滾都鼠標(biāo)滾輪所得到的那種效果(注意網(wǎng)頁右邊的滾動條):
(四)使用插件實現(xiàn)頁面的滾動加載
有一些插件可用,比如django-infinite-scroll-pagination、
也能使用一些純JavaScript插件來實現(xiàn),比如Waypoints
滾動分頁的具體代碼就不展示了,請參考文檔,這里就看一下簡單的效果:
總結(jié)
- 上一篇: 【Android Studio3.2安装
- 下一篇: 无陌然个人倒计时引导页源码