[Django 30Days] Day11 Blog實作範例一利用Paginator實現分頁功能

SH Tseng
Leonard like a robot. Software, Data, Life Note
10 min readNov 19, 2020

D11 透過django 分頁器來實現文章列表的分頁吧!

source: 火影忍者

Paginator

為什麼需要分頁器?
若是一個blog完整完成,文章會越來越多,若是把一百篇文章全部塞在同一頁的話,版面會過長、網頁載入時間也會過長,因此需要分頁器來將文章部分切分。

Django provides a few classes that help you manage paginated data — that is, data that’s split across several pages, with “Previous/Next” links. These classes live in django/core/paginator.py.

而分頁器支援許多方法,都可以在官方doc或是paginator.py中的類別找到,下面將會使用並講解部分方法。

而在使用分頁器前請先到myblog/models.py將資料按照時間排序的邏輯加入BlogPost此models底下。

#myblog/models.py
class BlogPost(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
summary = models.CharField(max_length=200, blank=True)
category = models.ForeignKey(BlogPostCategory, on_delete=models.CASCADE)
tags = models.ManyToManyField(BlogPostTag, blank=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def show_tags(self):
return ",".join([t.name for t in self.tags.all()])
def get_category(self):
return self.category.name
def __str__(self):
return f"<Blog: {self.title}>"
class Meta:
managed = True
db_table = "blog_post"
ordering = ['-created_at']
更改好後要記得更新資料庫。
python manage.py makemigrations
python manage.py migrate

新增資料

首先透過admin後台或是用shell多新增一些文章資料測試。
python manage.py shell
from myblog.models import *
dir() #查看環境有什麼東西
BlogPost.object.all()#看目前有幾篇文章
blog_post = BlogPost()
blog_post
blog_post.title="信號"
blog_post.content = "內容"
blog_post.summary = "摘要"
BlogPostCategory.object.all()#看有幾個類別
blog_post.category = BlogPostCategory.objects.all()[5]
由於作者是使用Django內建的使用者模型因此要import
from django.contrib.auth.models import User
User.objects.all() #查看使用者
blog_post.author = User.objects.all()[0]
儲存
blog_post.save()
要一次新增好幾篇則可以使用for迴圈,
for i in range(1, 10):
blog_post = Blog()
blog_post.title = f"迴圈 {i}"
blog_post.content = f"內容 {i}"
blog_post.category = category
blog.author = user
blog.save()
BlogPost.objects.all().count() #查看目前有幾篇可以暫時使用這樣的方式持續新增測試文章。

分頁器使用

從前端發送request請求分頁內容,後端處理request並response前端發送的request。

#shell
Paginator -> paginator = Paginator(文章列表, 一頁要放幾篇文章)
-> paginator.page(1)
from django.core.paginator import Paginator
blog_post = BlogPost.objects.all()
paginator = Paginator(blog_post, 5) #每五篇分頁一次
dir(paginator) #查詢可使用方法
paginator.count #共幾篇文章
paginator.num_pages #共分幾頁
paginator.page_rage #頁碼
取第一頁
page1 = paginator.page(1)
page1.object_list #查詢page1有幾篇
page1.object_list.count() #計算有幾篇
#修改myblog/views.py
from django.core.paginator import Paginator
class PostListView(View):
template_name = 'myblog/post_list.html'
def get(self, request, *args, **kwargs):
post_list = BlogPost.objects.all().order_by("-created_at")
paginator = Paginator(post_list, 5) #每5篇文進行分頁
page_num = request.GET.get('page', 1) #get url 頁面參數
page_of_posts = paginator.get_page(page_num)

context = {}
context['posts'] = page_of_posts.objects_list
context['page_of_posts'] = page_of_posts
context['category_list'] = BlogPostCategory.objects.all()
return render(request, self.template_name, context)

行21:由於已經文章存進分類器,因此直接對分類器做計算就可以算處有幾篇文章。
行23: 改成迭代每頁文章列表。
行37~50:到https://getbootstrap.com/docs/4.5/components/pagination/挑選一個喜歡的分頁樣板套入,並迭代頁碼部分。

頁碼實現進階修改

<!--頁碼實現--><ul class="pagination">
<li class="page-item">
<!--判斷是否有上一頁-->
{% if page_of_posts.has_previous %}
<a class="page-link" href="?page={{
page_of_posts.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span></a>
{% else %}
<span class="page-link" aria-hidden="true">&laquo;</span>
{% endif %}
</li>
{% for page_num in page_of_posts.paginator.page_range%}
<li class="page-item"><a class="page-link" href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endfor %}
<!--是否有下一頁 -->
<li class="page-item">
<!--判斷是否有上一頁-->
{% if page_of_posts.has_next %}
<a class="page-link" href="?page={{ page_of_posts.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>

{% else %}
<span class="page-link" aria-hidden="true">&raquo;</span>
{% endif %}
</li>
</ul>
1.if previous_page_number 如果有上一頁就可以點往上一頁,else 不能點。
2.if next_page_number 如果有下一頁就可以點往下一頁,else 不能點。

Paginator, Page方法整理

Paginator.get_page(int): 返回Page具有給定的基於1的index的object,同時還處理超出範圍和無效的頁碼。如果頁面不是數字,則返回第一頁。如果頁數為負或大於頁數,則返回最後一頁。
Paginator.count: 頁面上所有object總數。
Paginator.num_pages: 總頁數。
Paginator.page_range: 頁面範圍迭代器。
Page.has_next(): 是否有下一頁。
Page.has_previous(): 是否有上一頁。
Page.next_page_number(): 前往下一頁,若不存在則引發InvalidPage
Page.previous_page_number():
前往上一頁,若不存在則引發InvalidPagePage.object_list: 此頁面上的object列表。
更多請查看官方doc。

Reference:

楊仕航部落格

Django documentation

--

--