django | 18. ページング機能の追加


ページング関数の作成

# views.py
...
from django.core.paginator import Paginator
...

def question_list(request):
  questions = Question.objects.order_by('-create_date') # Question 모델 데이터를 작성일시의 역순(-)으로 정렬한다.
  # Paging 기능 구현하기
  page = request.GET.get('page', '1') # GET 방식 요청 URL에서 page값을 가져올 때 사용(?page=1). page 파라미터가 없는 URL을 위해 기본값으로 1을 지정한 것
  paginator = Paginator(questions, 5) # Paginator 클래스는 questions를 페이징 객체 paginator로 변환. 페이지당 5개씩 보여주기
  page_obj = paginator.get_page(page) # page_obj 객체에는 여러 속성이 존재
  context = { 'questions_list' : page_obj } # page_obj를 question_list에 저장한다.
  return render(request, 'home/question_list.html', context)

5個表示に設定し、5個のみ表示します.page_objオブジェクトには、次の属性があります.
項目説明ページング.count投稿総数paginator.per page各ページに表示する投稿数paginator.Page rangeページ範囲番号現在ページ番号preve page number前ページ番号next page number次ページ番号has next次ページstart index現在ページ開始インデックス(1から)end index現在ページ終了インデックス(1から)

ページングの適用

<!-- question_list.html -->

...
</table>
<ul>
  <!-- 이전 페이지가 있으면 -->
  {% if questions_list.has_previous %}
  <a href="?page={{ questions_list.previous_page_number }}">이전</a>
  {% endif %}
  <!-- 페이지 리스트 -->
  {% for page_number in questions_list.paginator.page_range %}
  <!-- 현재 페이지 번호랑 같다면 style을 다르게 줄 수 있다. -->
  {% if page_number == question_list.number %}
  <li>
    <a href="?page={{ page_number }}">{{ page_number }}</a>
  </li>
  {% else %}
  <li>
    <a href="?page={{ page_number }}">{{ page_number }}</a>
  </li>
  {% endif %}
  {% endfor%}
  <!-- 다음 페이지가 있으면 -->
  {% if questions_list.has_next %}
  <a href="?page={{ questions_list.next_page_number }}">다음</a>
  {% endif %}
</ul>
<!-- <a href="{% url 'home:question_create' %}">질문 등록하기</a> -->
<!-- 모델 폼 이용하지 않을 경우 -->
<a href="{% url 'home:question_new' %}">질문 등록하기</a>

{% endblock %}
テンプレートで使用される{{ questions_list }}がビューです.pyファイルのpage obj.言い換えれば、テンプレートの{{ questions_list.previous_page_number }}は`{page obj.preved page number}}と同じである.

ページ制限機能の実装


現在の問題


任意の300個のデータを入れます.
$ python manage.py shell
>>> from home.models import Question
>>> for i in range(300):
...     Question.objects.create(subject="test data [%03d]" %i, content="test data")  
... 
これで300個のデータがあります.ここでの問題点を下図に示します.

ページボタンが多すぎます.

解決策

<!-- question_list.html -->

...
  <!-- 페이지 리스트 -->
  {% for page_number in questions_list.paginator.page_range %}
  <!-- 코드 추가하기 : 페이지 표시 제한 기능 구현하기 -->
  {% if questions_list.number|add:5 >= page_number and page_number >= questions_list.number|add:-5 %}

...
{% if questions_list.number|add:5 >= page_number and page_number >= questions_list.number|add:-5 %}:現在のページを基準にして、左右各5個を表示します.比questions_list.numberは、5以上の値のみを表示します.|add:-5は5|add:5を減算して5を減算することを示す.

今は8ページ目で、ページ番号は3から13です.

テンプレートフィルタの直接作成


テンプレートフィルタとは、テンプレートタグの|文字後に使用されるフィルタです.例えば、Noneではなく空白文字列を表示するためのdefault_if_noneをテンプレートフィルタと呼ぶ.
{{ form.subject.value|default_if_none:''||

現在の問題



各ページに問題があり、投稿番号は常に1から始まります.

解決策


1.投稿番号式の作成


12個の質問投稿(ページごとに10個表示)がある場合は、1ページ目に12~30個目の投稿を表示し、2ページ目には逆順序の2~1個目の投稿を表示する必要があります.質問投稿の番号を逆順序で並べ替えるには、次の式を適用する必要があります.
일련번호 = 전체 게시물 개수 - 시작 인덱스 - 현재 인덱스 + 1
開始インデックスは、各ページから始まる投稿の開始番号を表します.たとえば、各ページに10の投稿が表示されている場合、最初のページの開始インデックスは11で、2番目のページの開始インデックスは11です.現在のインデックスは0から1の番号で、ページに表示される投稿の数と同じです.
したがって,全スレッドが12個あり,1ページにつき10個のスレッドが表示されると,1ページ目のシリアル番号は12 - 1 - (0 ~ 9 반복) + 1,12~3,2ページ目は12 - 11 - (0~1 반복) + 1,2~1が表示される.
テンプレートにこの式を適用するには、マイナス記号機能が必要です.前にプラスフィルター(|add:5)を使用したように、マイナスフィルターがあれば良いのですが、長い間マイナスフィルターはありません.そこで、マイナスフィルタを作成します.|add:-5のように、数値を直接入力すると、プラスフィルタを使用して必要な値を減算した結果を画面に表示できます.しかし、この方法はここでは使えません.プラス記号フィルタに変数を適用できないためです.

2.テンプレートフィルタディレクトリの作成


テンプレートフィルタ関数は、テンプレートフィルタファイルを作成した後に定義する必要があります.テンプレートフィルタファイルは、テンプレートファイルと同様に、新しいhome/templatetagsディレクトリを作成して保存する必要があります.またtemplatetagsディレクトリはappディレクトリの下に作成する必要があります.
正式な書類によれば、ディレクトリ内には__init__.pyファイルが必要です.

3.テンプレートフィルタの作成

# home_filter.py

from django import template

register = template.Library()

@register.filter
def sub(value, arg):
  return value - arg
テンプレートフィルタ関数を作成する方法は、sub関数に@register.filterという注釈を適用すると、テンプレートでフィルタとして使用できます.テンプレートフィルタ関数subは、入力した値argを既存の値から減算して返します.

4.テンプレート・フィルタの使用


独自に作成したテンプレートフィルタを使用するには、{% load home_filter %}などのテンプレートフィルタファイルをテンプレートファイルの上部にロードする必要があります.テンプレートファイルの上部にextends文がある場合は、load文はextends文の後にある必要があります.
<!-- question_list.html -->

{% extends 'base.html' %}
<!-- 추가 -->
{% load home_filter %} 
{% block content %}

...
  <tbody>
    {% if questions_list %}
    {% for question in questions_list %}
    <tr>
      <!-- 추가 -->
      <td>{{ questions_list.paginator.count|sub:questions_list.start_index|sub:forloop.counter0|add:1 }}</td>
...
正式要素説明問題リスト.paginator.count完全投稿カウント問題list.start index開始インデックス(1から)forloop.カウンタ0リングの現在のインデックス(forloop.counter 0は0から、forloop.counterは1から)

問題が解決した.