3-2. 페이징(Paging)
현재 파이보의 질문 목록은 페이징(Paging) 처리가 안되어 게시글 300개 작성할 경우 300개의 게시글이 모두 조회된다.
대량의 테스트 데이터 만들기
페이징 테스트를 위해 대량의 데이터를 임의로 만들어보자.
> Django shell
(mysite) c:\projects\mysite>python manage.py shell Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 22:45:29) [MSC v.1916 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> |
질문 데이터 생성을 위해 모듈 임포트
>>> from pybo.models import Question >>> from django.utils import timezone |
질문 300개를 추가해주자. 두번째 코드는 앞 띄어쓰기 4칸이 있으니 체크바란다.
>>> for i in range(300): ... q = Question(subject='테스트 데이터입니다:[%03d]' % i, content='내용무', create_date=timezone.now()) ... q.save() ... >>> |
웹사이트에 가보니 아래와 같이 나타났다.
Paginator
장고에서는 페이징을 위해 Paginator 클래스를 사용한다. Paginator 클래스를 사용해서 다음과 같이 index 함수에 페이징 기능을 적용할 수 있다.
> ../projects/mysite/pybo/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm, AnswerForm
from django.core.paginator import Paginator
def index(request):
"""
pybo 목록출력
"""
# 입력 파라미터
page = request.GET.get('page', '1') # 페이지
# 조회
question_list = Question.objects.order_by('-create_date')
# 페이징처리
paginator = Paginator(question_list, 10) # 페이지당 10개씩 보여주기
page_obj = paginator.get_page(page)
context = {'question_list': page_obj}
context = {'question_list': question_list}
return render(request, 'pybo/question_list.html', context)
(... 생략 ...)
Index 함수를 자세히 살펴보자.
Page = request.GET.get(‘page’,’1’)은 http://localhost:8000/pybo/?page=1 과 같이 GET방식으로 호출된 URL에서 page값을 가져올 때 사용한다. http://localhost:8000/pybo/ 처럼 page값이 없이 호출된 경우 디폴트로 1이라는 값을 설정한다.
Paginator 클래스를 다음과 같이 사용했다.
Paginator = Paginator(question_list, 10) #페이지당 10개씩 보여주기 |
Paginator의 첫번째 파라미터 question_list는 게시물 전체를 의미하는 데이터다. 두번째 파라미터인 10은 페이지당 보여줄 게시글의 개수이다.
Page_obj = paginator.get_page(page) |
Paginator 이용해 요청된 페이지(page)에 해당하는 페이징 객체(page_obj)을 생성했다. 이로 인해 장고 내부적으로 데이터 전체가 아닌 해당 페이지의 데이터만 조회하도록 쿼리가 변경된다.
페이징 객체(page_obj)에는 아래와 같은 속성이 있다.
항목 : 설명
- Paginator.count : 전체 게시물 개수
- Paginator.per_page : 페이지당 보여줄 게시물 개수
- Paginator.page_range : 페이지 범위
- Number : 현재 페이지 번호
- Previous_page_number : 이전 페이지 번호
- Next_page_number : 다음 페이지 번호
- Has_previous : 이전 페이지 유무
- Has_next : 다음 페이지 유무
- Start_index : 현재 페이지의 시작 인덱스(1부터 시작)
- End_index : 현재 페이지의 끝 인덱스(1부터 시작)
템플릿에 페이징 적용하기
Index 함수에서 질문 목록 템플릿(pybo/question_list.html)에 전달한 데이터(context)는 아래와 같다.
context = {'question_list': page_obj} return render(request, 'pybo/question_list.html', context) |
질문 목록 템플릿에 전달된 페이징 객체는 question_list다. 페이징 객체 question_list 이용해 템플릿에서 어떻게 페이징 처리하는지 알아보자.
> ../projects/mysite/templates/pybo/question_list.html
Question_list.html 템플릿 파일의 </table> 태그 밑에 아래 코드를 추가해주자.
(... 생략 ...)
</table>
<!-- 페이징처리 시작 -->
<ul class="pagination justify-content-center">
<!-- 이전페이지 -->
{% if question_list.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ question_list.previous_page_number }}">이전</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" tabindex="-1" aria-disabled="true" href="#">이전</a>
</li>
{% endif %}
<!-- 페이지리스트 -->
{% for page_number in question_list.paginator.page_range %}
{% if page_number == question_list.number %}
<li class="page-item active" aria-current="page">
<a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
</li>
{% endif %}
{% endfor %}
<!-- 다음페이지 -->
{% if question_list.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ question_list.next_page_number }}">다음</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" tabindex="-1" aria-disabled="true" href="#">다음</a>
</li>
{% endif %}
</ul>
<!-- 페이징처리 끝 -->
<a href="{% url 'pybo:question_create' %}" class="btn btn-primary">질문 등록하기</a>
</div>
{% endblock %}
코드를 한번 살펴보자. 이전 페이지가 있는 경우 “이전” 링크가 활성화되고, 이전 페이지 없는 경우 “이전”링크가 비활성화 된다. 다음 페이지도 마찬가지다. 페이지 리스트를 루프 돌면서 해당 페이지로 이동할 수 있는 링크를 생성해다. 현재 페이지와 같은 경우 active 클래스로 강조표시를 했다.
페이징 기능 | 코드 |
이전 페이지가 있는지 체크 | {% if question_list.has_previous %} |
이전 페이지 번호 | {{ question_list.previous_page_number }} |
다음 페이지가 있는지 체크 | {% if question_list.has_next %} |
다음 페이지 번호 | {{ question_list.next_page_number }} |
페이지 리스트 루프 | {% for page_number in question_list.paginator.page_range %} |
현재 페이지와 같은지 체크 | {% if page_number == question_list.number %} |
페이지 리스트를 보기 좋기 표시하기 위해 부트스트랩의 pagination 컴포넌트를 이용했다. Pagination, page-item, page-link 등이 부트스트랩 pagination 컴포넌트의 클래스다.
확인해보면 아래와 같이 페이지가 표시된다. 하지만 모든 페이지가 한번에 표시되어 깔끔하지 못한 느낌이 있다.
> ../projects/mysite/templates/pybo/question_list.html
위 문제를 해결하기 위해 아래와 같이 템플릿을 수정하자.
(... 생략 ...)
<!-- 페이지리스트 -->
{% for page_number in question_list.paginator.page_range %}
{% if page_number >= question_list.number|add:-5 and page_number <= question_list.number|add:5 %}
{% if page_number == question_list.number %}
<li class="page-item active" aria-current="page">
<a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ page_number }}">{{ page_number }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
(... 생략 ...)
아래 코드가 페이지 표시 제한 기능의 핵심 코드다.
{% if page_number >= question_list.number|add:-5 and page_number <= question_list.number|add:5 %} (... 생략 ...) {% endif %} |
여기서 |add:-5, |add:5 는 템플릿 필터다. |add:-5는 5만큼 빼라는 의미고, |add:5는 5만큼 더하라는 의미다. 위 코드는 리스트가 현재 코드 기준으로 좌우 5개씩 보이게 만든다. 현재 페이지를 의미하는 question_list.number 보다 5만큼 크거나 5만큼 작은 값만 표시되도록 만들었다.
'Research > Django' 카테고리의 다른 글
[Django] 점프 투 장고 튜토리얼 - 03-4. 답변 개수 표시 (0) | 2021.11.16 |
---|---|
[Django] 점프 투 장고 튜토리얼 - 03-3. 템플릿 필터 (0) | 2021.11.16 |
[Django] 점프 투 장고 튜토리얼 - 03-1. 네비게이션바 (0) | 2021.11.16 |
[Django] 점프 투 장고 튜토리얼 - 02-10. 폼 (0) | 2021.11.15 |
[Django] 점프 투 장고 튜토리얼 - 02-9. 템플릿 상속 (0) | 2021.11.15 |
댓글