Djangoでページング処理を実装するには、views.pyにgeneric.ListViewのpaginate_byを使用すれば簡単に実装できます。
しかし、新機能実装の都合上generic.ListViewが使えなかったため(※)、ListViewなしでページング処理を実装する方法をメモ書きします。
※ returnにrenderを使用したかった。今回の課題はpaginate_byとrenderの併用です。
ページング処理とは
テンプレートでリストに定義した一覧をfor文で表示する際に、全てのリストを同じページに表示したくない時などに利用します。(ブログの記事一覧など)
表示しなかったリストは、ページングの[ 1 2 3 4 5...]といった表示の数字をクリックすると後続のリストを表示できます。
generic.ListViewを利用した実装例
generic.ListViewを利用する場合は、views.pyにこれだけ定義すればページング処理を利用できるようになります。
from django.views import generic
from .models import Post
class IndexView(generic.ListView):
model = Post
paginate_by = 10 # ページング処理。リスト中の10行を表示する。
関連ファイルのコード
以下はページング処理を実装するために必要な関連ファイルのコードです。
generic.ListViewを使わずにページング処理を実装する場合、上記のviews.pyとurls.pyの内容を書き換えます。他のファイルの内容は変わりません。
from django.urls import path
from . import views
app_name = 'app'
urlpatterns = [
# URLにhttp://127.0.0.1:8000/と入力するとviews.pyのIndexView関数に遷移する
path('', views.IndexView.as_view(), name='index'),
]
from django.db import models
class Post(models.Model):
# 記事タイトルカラム。今回はこちらに90件分のタイトルを登録済み
title = models.CharField('タイトル', max_length=255)
<!-- Models.pyで定義しているPOSTのカラムtitleをレコード分だけ取り出す -->
{% for post in post_list %}
<div>{{ post.title }}</div>
{% endfor %}
<br>
<!-- ページング処理の部分 -->
<!-- 「前へ」の部分 -->
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">前へ</a>
{% endif %}
<!-- 数字部分 -->
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<span>{{ num }}</span>
{% else %}
<a href="?page={{ num }}">{{ num }}</a>
{% endif %}
{% endfor %}
<!-- 「次へ」 の部分 -->
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">次へ</a>
{% endif %}
generic.ListViewを利用しない実装例
今度は本題のgeneric.ListViewを利用しない実装例です。
views.pyを次のように書き換えます。
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
from .models import Post
def index_view(request):
# models.pyからPostのすべてのデータを取得
queryset = Post.objects.all()
# paginate_byの実装
# 1ページに表示させるtitle数を指定する
count = 10
# pagenateの実行
paginator = Paginator(queryset, count)
page = request.GET.get('page')
try:
page_obj = paginator.page(page)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)
return render(request,
'app/post_list.html',
{'post_list': page_obj.object_list,
'page_obj': page_obj}
)
from django.urls import path
from . import views
app_name = 'app'
urlpatterns = [
# path部分を次のように書き換える
path('', views.index_view, name='index'),
# path('', views.IndexView.as_view(), name='index'),
]
解説
def index_view(request):
# models.pyからPostのすべてのデータを取得
queryset = Post.objects.all()
Post.objects.all()
でPostに格納されている全てのデータを取り出し、querysetに格納しています。
querysetとは、Djangoが準備した型のことです。
pythonにはstr型やint型などの型がありますが、querysetはDjangoオリジナルの型です。
表示するデータの順番を変えたい場合は以下のように記述することで、昇順、降順に並び替えることができます。
queryset = Post.objects.order_by('title')
queryset = Post.objects.order_by('-title')
# paginate_byの実装
# 1ページに表示させるtitle数を指定する
count = 10
# pagenateの実行
paginator = Paginator(queryset, count)
page = request.GET.get('page')
count = 10に、1ページに表示させたいtitleの数を指定します。
それを先ほど取得したquerysetのデータを含め、Paginator関数に渡します。
Paginator関数ページング処理の肝となる部分です。querysetとページ数(int)を引数として利用します。
page = request.GET.get('page')は現在ブラウザに表示しているページ数を取得する処理です。
ページング処理を使用すると、URLに次のようなパラメータが付与されます。
?page={{ num }}
現在のページが7ページなら次のようになります。
http://127.0.0.1:8000/?page=7
上記の例でいうと、 request.GET.get('page')は「7」を取得してpageに格納しています。
try:
page_obj = paginator.page(page)
except PageNotAnInteger:
page_obj = paginator.page(1)
except EmptyPage:
page_obj = paginator.page(paginator.num_pages)
Paginatorインスタンスのpage関数を利用し、pageオブジェクトを取得しています。
始めにページへアクセスするときはパラメータ?page={{ num }}
が存在しない、つまりpageに現在のページ(int)が格納次の部分が実行されます。
except PageNotAnInteger:
page_obj = paginator.page(1)
この場合、Postモデルの1ページ目のデータがpage_objに格納されます。
2回目以降はtry部分が実行され、テンプレートから受け付けたページ数がpageに格納されてpaginator.page(page)が実行されます。
return render(request,
'app/post_list.html',
{'post_list': page_obj.object_list,
'page_obj': page_obj}
)
最後にrenderでpage_objのデータをpost_list.htmlに渡しています。
page_obj.object_listは、Postモデルの1ページ目のtitleのリストです。
page_objは、テンプレート側でページングを制御するのに必要な情報が入っています。
render()の部分に任意の「キー:値」を設定することで、ページング以外の情報をテンプレートに渡すことが可能です。(今回著者が一番やりたかったこと)
return render(request,
'app/post_list.html',
{'post_list': page_obj.object_list,
'page_obj': page_obj,
# 追加でhogeリストをテンプレートへ渡している
'hoge_list': hoge_list}
)
解説は以上です。