[Django 30Days] Day11 Blog實作範例一利用Paginator實現分頁功能
D11 透過django 分頁器來實現文章列表的分頁吧!
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 shellfrom 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 Paginatorclass 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">«</span></a> {% else %}
<span class="page-link" aria-hidden="true">«</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">»</span>
</a>
{% else %}
<span class="page-link" aria-hidden="true">»</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():InvalidPage。Page.object_list:
此頁面上的object列表。
更多請查看官方doc。
Reference:
楊仕航部落格
Django documentation