쟝고에는 페이징 데이터를 쉽게 관리할 수 있는 페이징을 휘한 클래스가 내장되어 있다.
페이지당 반환할 객체 수를 정할 수 있고, 사용자가 요청한 페이지에 해당하는 게시글들을 조회할 수 있다.
blog의 views.py 파일을 편집해 Paginator
클래스를 불러오고 아래와 같이 수정해 보자.
def post_list(request):
post_list = Post.published.all()
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
posts = paginator.page(page_number)
return render(request, 'blog/post/list.html', {'posts': posts})
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
posts = paginator.page(page_number)
사용자가 다른 페이지들을 조회할 수 있도록 페이지 네비게이션을 만들어야 한다.
페이징 링크를 표시하는 템플릿을 생성하고, 웹사이트에서 다른 객체들의 페이징에서도 템플릿을 재사용할 수 있도록 일반화해 보자.
templates/ 디렉토리에 pagination.html 파일을 만들고 코드를 작성해보자.
<div class="pagination">
<span class="step-links">
{% if page.has_previous%}
<a href="?page={{page.previous_page_number}}"> Previous</a>
{% endif %}
<span class="current">
Page {{page.number}} of {{page.paginator.num_pages}}
</span>
{% if page.has_next%}
<a href="?page={{page.next_page_number}}"> Next</a>
{% endif %}
</span>
</div>
이 템플릿은 페이징을 일반화한 템플릿으로,
템플릿은 이전과 다음 링크를 렌더링하고 현재 페이지와 결과의 총페이지를 표시하기 위한 Page 객체를 context에 가지고 있어야 한다.
아래와 같이 blog/post/list.html 템플릿으로 돌아가 content 블럭 하단에 pagenation.html 템플릿을 포함시키자.
{% include "pagination.html" with page=posts %}
include
템플릿 태그는 지정된 템플릿을 불러와 현재 템플릿 컨텍스트를 사용해 렌더링한다.
그리고 추가 context 변수를 전달하기 위해 with을 사용해 posts 객체를 넘겨 준다.
이제 브라우저에서 http://localhost:8000/blog
경로를 확인하면 게시글 목록 하단에 내비게이션 링크가 표시되게 된다.
Next를 클릭하면 마지막 게시글이 표시되는데, 두 번째 page의 URL에는 ?page=2 처럼 GET 매개 변수가 포함되어 있다. 이 매개변수는 뷰에서 paginator를 통해 요청된 결과 페이지를 가져오는 데 사용된다.
이제 뷰에서 페이징 오류에 대한 예외 처리를 추가해 보자.
page 파라미터는 지정된 페이지를 검색하기 위해 뷰에서 사용되는데, 존재하지 않는 페이지 번호라 페이지 번호로 쓸 수 없는 문자열과 같이 잘못된 값으로 인해 오류가 발생할 수 있다.
이런 경우 적절한 오류 처리를 구현해야 한다.
브라우저에 http://localhost:8000/blog/?page=9999를 열어보면 아래와 같은 오류 페이지가 표시될 것이다.
Paginator 객체는 9999페이지를 조회할 때 범위를 벗어날 때 EmptyPage
에러를 발생시킨다.
뷰에서 이 오류를 처리해 보자.
views.py 파일을 열어 EmptyPage
를 import하고 post_list 뷰를 아래와 같이 수정해보자.
from django.core.paginator import Paginator, EmptyPage
def post_list(request):
post_list = Post.published.all()
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
try:
posts = paginator.page(page_number)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request, 'blog/post/list.html', {'posts': posts})
페이지를 검색할 때 예외를 처리하기 위해 try, except
블록을 추가했다.
요청한 페이지가 범위를 벗어나면 paginator.num_pages
로 총 페이지수를 얻어 마지막 페이지를 반환한다.
총 페이지 수는 마지막 페이지 번호와 동일하다.
이제 뷰에서 다시 http://localhost:8000/blog/?page=9999에 접속할 경우 예외를 처리해 페이지네이션 결과의 마지막 페이지를 반환한다.
page 매개 변수에 정수가 아닌 다른 것이 전달되는 경우에도 뷰가 처리해야 한다.
브라우저에서 http://localhost:8000/blog/?page=qwer 를 접속해 보자.
이 경우 페이지 번호는 정수만 될 수 있기 때문에 PageNotAnInteger
예외가 발생하게 된다.
뷰에서 이 오류를 처리해 보자.
views.py 파일을 편집해 PageNotAnInteger
를 import하고 아래와 같이 post_list 뷰를 수정하자.
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def post_list(request):
post_list = Post.published.all()
paginator = Paginator(post_list, 3)
page_number = request.GET.get('page', 1)
try:
posts = paginator.page(page_number)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request, 'blog/post/list.html', {'posts': posts})
페이지를 조회할 때 PageNotAnInteger
에러를 처리하기 위해 새로운 except 블록을 추가했다.
요청한 페이지가 정수가 아닌 경우 결과의 첫 번째 페이지를 반환한다.
다시 http://localhost:8000/blog/?page=qwer 페이지를 방문하면,
뷰에서 예외를 처리해 결과의 첫 번째 페이지가 반환되게 된다.