一、問題描述
針對上一篇章的批次插入資料,我們會發現一個很嚴重的問題,將所有資料都放到前端頁面展示的時候一千多條資料放在了一頁,這樣太不方便,就像書本一樣,不可能把所有內容都放在一頁吧。
所以我們可以也想書本一樣,嘗試做分頁處理
二、分頁推導
首先需要明確的是,get請求/post請求都可以攜帶引數,所以在朝後端傳送資料時,可以攜帶一個引數告訴後端我們想看第幾頁的資料。
還有一點就是,Queryset物件支援索引和切片操作,但是不支援負數索引情況。
1、第一頁分析
book_list = models.Book.objects.all()
# 獲取使用者想訪問的頁碼 如果沒有 預設展示第一頁
current_page = request.GET.get("page",1)
# 由於後端接受到的前端資料是字串型別所以我們這裡做型別轉換處理加異常捕獲
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
# 還需要定義頁面到底展示幾條資料
per_page_num = 10 # 一頁展示10條資料
# 需要對總資料進行切片操作 需要確定切片起始位置和終止位置
start_page = ?
end_page = ?
然後需要研究起止頁的位置,也就是current_page、per_page_num、start_page、end_page四個引數之間的資料關係
2、整體分析
current_page、per_page_num、start_page、end_page四個引數之間的資料關係
per_page_num = 10
current_page start_page end_page
1 0 10
2 10 20
3 20 30
4 30 40
per_page_num = 5
current_page start_page end_page
1 0 5
2 5 10
3 10 15
4 15 20
start_page = (current_page - 1) * per_page_num
end_page = current_page* per_page_num
- 可以很明顯的看出規律
start_page = (current_page - 1) * per_page_num
end_page = current_page* per_page_num
3、手動切片
(1)後端
def ab_many(request):
# 分頁
book_list = models.Book.objects.all()
# 想訪問哪一頁
current_page = request.GET.get('page', 1) # 如果獲取不到當前頁碼,就展示第一頁
# 資料型別轉換
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 每頁展示多少條
per_page_num = 10
# 起始位置
start_page = (current_page - 1) * per_page_num
# 終止位置
end_page = current_page * per_page_num
# 計算總共需要多少頁
all_count = book_list.count()
# 增加高光點(當前頁展示)
page_count, more = divmod(all_count, per_page_num)
if more:
page_count += 1
page_html = ''
xxx = current_page
if current_page < 6:
current_page = 6
for i in range(current_page-5,current_page+6): # 頁碼我們需要從1開始
if xxx == i:
page_html += f'<li class="active"><a href="?page={i}">{i}</a></li>'
else:
page_html += f'<li><a href="?page={i}">{i}</a></li>'
book_queryset = book_list[start_page:end_page]
return render(request, 'ab_many.html', locals())
(2)前端
{% for book_obj in book_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
(3)路由訪問
透過在 url 後面攜帶引數,完成分頁操作
http://127.0.0.1:8000/ab_many/?page=3
三、自定義分頁器的使用
當我們需要使用到非django內建的第三方功能或者元件程式碼的時候,我們一般情況下會建立一個名為utils資料夾,在該檔案內對模組進行功能性劃分。
utils可以在每個應用下建立。
我們到了後期封裝程式碼的時候,不再侷限於函式,還是儘量朝物件導向去封裝我們自定義的分頁器
1、自定義分頁器封裝程式碼(模版)
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封裝分頁相關資料
:param current_page: 當前頁
:param all_count: 資料庫中的資料總條數
:param per_page_num: 每頁顯示的資料條數
:param pager_count: 最多顯示的頁碼個數
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 總頁碼
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果總頁碼 < 11個:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 總頁碼 > 11
else:
# 當前頁如果<=頁面上最多顯示11/2個頁碼
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 當前頁大於5
else:
# 頁碼翻到最後
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 新增前面的nav和ul標籤
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首頁</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
else:
next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部新增標籤
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
後端透過get方式實現分頁功能,每次切換頁的時候,會重新整理頁面。
後端透過post方式實現分頁功能,每次切換頁的時候,不會重新整理頁面,只會重新渲染下一頁的內容。
2、get方法實現分頁器功能(我比較常用)
(1)後端
from utils.mypage import Pagination
def get_book(request):
book_list = models.Book.objects.all()
current_page = request.GET.get("page",1)
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 前端傳來的資料為字串,所以後端異常捕獲,為字串的話轉為int型別
all_count = book_list.count()
page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10)
page_queryset = book_list[page_obj.start:page_obj.end]
return render(request,'booklist.html',locals())
(2)前端
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
{% for book in page_queryset %}
<p>{{ book.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
# ## 只需要這一句就可以,上面的的for迴圈是要分頁的資料
</div>
</div>
</div>
3、post方法實現分頁器功能
(1)前端
評論區分頁開始
<body>
<div class="pull-right">
{{ page_obj.page_html|safe }}
</div>
</body>
<script>
// 評論列表分頁繫結點選事件
$('.btn_page').click(function () {
// 獲取當前點了第幾頁
var current_page = $(this).attr('current_page')
var article_id = {{ article_obj.pk }}
$('.active').removeClass('active');
$(this).parent().addClass('active');
$.ajax({
url: '/comment_page/',
type: 'post',
data: {
current_page: current_page,
'article_id': article_id,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function (args) {
let html = '';
if (args.code == 200) { //重新渲染一遍樓層頁面,
$.each(args.data, function (index, obj) {
html +=`<li class="list-group-item">
<span style="margin-right: 10px"># ${obj.forloop}樓</span>
<span style="margin-right: 10px">${obj.comment_time}</span>
<span style="margin-right: 10px">${obj.username}</span>
<span style="margin-right: 10px" class="pull-right"><a href="javascript:;"
comment_username="${obj.username}"
comment_id='${obj.pk}'
class="reply">回覆</a></span>
<div class="content" style="margin-left: 14px">
${obj.content}
</div>
</li>`
});
$('.list-group').html(html);
}
}
})
})
</script>
(2)後端
如果整個專案中多個地方需要用到分頁功能,可以將上面的固定模板加入到utils資料夾,utils資料夾內可以放一些通用 的輔助函式,工具類和可重用的程式碼片段,這些片段不屬於特定的模型,檢視或者其他部分,可以被多個部分共享和複用。
# 評論區分頁
from utils.mypage1 import Pagination
page_obj = Pagination(current_page=request.GET.get('page', 1),
all_count=comment_list.count(), per_page_num=5)
comment_list = comment_list[page_obj.start:page_obj.end]
def comment_page(request):
# 根據當前第幾頁查詢當前頁的評論列表資料
if request.method == 'POST':
back_dic = {'code': 200, 'msg': '查詢成功'}
# 接收引數(點的第幾頁)
article_id = request.POST.get('article_id')
current_page = request.POST.get('current_page') # 2
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 每頁顯示的條數
per_page_num = 5
start_page = (current_page - 1) * per_page_num
end_page = current_page * per_page_num
# 查詢評論列表資料
comment_list = models.Comment.objects.filter(article_id=article_id).all()[start_page:end_page] # queryset物件
comment_list_obj = [] # [{},{},{}]
i = current_page * per_page_num - (per_page_num - 1) # 翻頁後順序依次相加
for comment in comment_list:
comment_list_obj.append({
'forloop': i,
'pk': comment.pk,
'comment_time': comment.comment_time,
'username': comment.user.username,
'content': comment.content
})
i += 1
back_dic['data'] = comment_list_obj
return JsonResponse(back_dic)