페이지 네이션을 만드는 방법을 알아본다.
먼저 뷰를 수정해야 한다.
장고의
페이지네이터
를 불러온다.
#views.py
from django.core.paginator import Paginator
post_list
함수를 수정한다.
views.py > post_list
def post_list(request):
posts = Post.objects.all().order_by("-dt_modified")
page = request.GET.get("page", 1)
paginator = Paginator(posts, 9)
page_obj = paginator.get_page(page)
context = {"page_obj": page_obj}
return render(request, 'posts/post_list.html', context=context)
요청으로부터
page 파라미터
에 인자를 넘겨받아변수 page
에 지정한다. 만약 넘겨받은 인자가 없다면 1로 간주한다.page = request.GET.get("page", 1)
변수 posts
의 데이터를 한 페이지 당 9개로 구분하고변수 paginator
에 지정한다.paginator = Paginator(posts, 9)
변수 paginator
를 통해 전체 데이터를 전체 데이터를 한 페이지 당 N개의 데이터로 나누고변수 page
와 일치하는 페이지 번호의 데이터들을 불러온 뒤변수 page_obj
에 지정한다.page_obj = paginator.get_page(page)
풀어서 설명하자면 아래와 같다.
page_obj
는
1. paginator를 통해 전체 데이터를 한 페이지 당 9개의 데이터로 구분 한 뒤
,
2. 템플릿으로부터 파라미터로 전달받은 page의 값이 2라면
,
3. 2페이지에 포함된 10번부터 18번까지의 데이터들을 호출한다.
context로 page_ojb
를 넘겨준다.context = {"page_obj": page_obj}
요청과 context를 전달하면서 템플릿을 렌더한다.
return render(request, 'posts/post_list.html', context=context)
우선 데이터를 호출하는 코드 중 기존
context
로 넘겨받던posts
를page_obj
로 변경했기 때문에 해당 부분을 변경해준다.
그런데
for
문에서page_obj
만 단독으로 사용하면 전체 게시글 개수, 전체 페이지 수, 이전 페이지 및 다음 페이지 존재 등은 확인할 수 있지만 데이터에는 접근할 수 없다. 따라서페이지 내에 있는 데이터에 접근
하기 위해서는.object_list
를 붙여줘야 한다.
<!--post_list.html-->
<div class="btn_post">
<a href={% url "post-create" %}>기록하기</a>
</div>
{% if page_obj %}
<div class="post_container">
{% for post in page_obj.object_list %}
<div class="post"><a href="{% url "post-detail" post.id %}">
<h2 class="title">{{post.title}}</h2>
<p class="date">{{post.dt_created}}</p>
<p class="text">{{post.content}}</p>
</a></div>
{% endfor %}
</div>
이후 페이지네이션을 표기하는 코드를 작성한다.
<!--post_list.html-->
<div class="pagination">
{% if page_obj.has_previous %}
<a class="able" href="?page=1"><<</a>
{% else %}
<a class="disable" onclick="return false;" href="#"><<</a>
{% endif %}
{% if page_obj.has_previous %}
<a class="able" href="?page={{ page_obj.previous_page_number }}"><</a>
{% else %}
<a class="disable" onclick="return false;" href="#"><</a>
{% endif %}
{% for page_number in page_obj.paginator.page_range %}
{% if page_number == page_obj.number %}
<a class="disable" id="current" onclick="return false;" href="#">{{ page_number }}</a>
{% elif page_obj.number <= 3 and page_number <= 5 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% elif page_obj.number > page_obj.paginator.num_pages|add:-3 and page_number >= page_obj.paginator.num_pages|add:-4 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% elif page_number >= page_obj.number|add:-2 and page_number <= page_obj.number|add:2 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a class="able" href="?page={{ page_obj.next_page_number }}">></a>
{% else %}
<a class="disable" onclick="return false;" href="#">></a>
{% endif %}
{% if page_obj.has_next %}
<a class="able" href="?page={{ page_obj.paginator.num_pages }}">>></a>
{% else %}
<a class="disable" onclick="return false;" href="#">>></a>
{% endif %}
</div>
무척 어려워 보이지만 전체적인 맥락을 파악하면 이해할 수 있다.
우리가 페이지네이션에서 만들어야할 것은 아래와 같다.
- 처음으로
- 이전으로
- 현재 페이지 기준 근접한 페이지
- 다음으로
- 마지막으로
먼저
처음으로
와이전으로
를 살펴본다.{% if page_obj.has_previous %} <a class="able" href="?page=1"><<</a> {% else %} <a class="disable" onclick="return false;" href="#"><<</a> {% endif %} {% if page_obj.has_previous %} <a class="able" href="?page={{ page_obj.previous_page_number }}"><</a> {% else %} <a class="disable" onclick="return false;" href="#"><</a> {% endif %}
처음으로
버튼은if
문을 사용해
1. 이전 페이지가 있다면
page 파라미터
에1
을 인자로 전달하는<a>
태그를 생성
2. 이전 페이지가 없다면
클릭해도반응이 없는 <a>
태그를 생성한다.
이전으로
버튼은if
문을 사용해
1. 이전 페이지가 있다면
page 파라미터
에이전 페이지의 page 번호
를 인자로 전달하는<a>
태그를 생성
2. 이전 페이지가 없다면
클릭해도반응이 없는 <a>
태그를 생성한다.
다음으로
와마지막으로
버튼을 이와 비슷하니 따로 설명하지 않겠다.
이 부분은
page_obj
에 사용된paginator
를 통해 페이지의전체 범위
를 반복해page_number
를 찾는다.
{% for page_number in page_obj.paginator.page_range %}
{% if page_number == page_obj.number %}
<a class="disable" id="current" onclick="return false;" href="#">{{ page_number }}</a>
{% elif page_obj.number <= 3 and page_number <= 5 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% elif page_obj.number > page_obj.paginator.num_pages|add:-3 and page_number >= page_obj.paginator.num_pages|add:-4 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% elif page_number >= page_obj.number|add:-2 and page_number <= page_obj.number|add:2 %}
<a class="able" href="?page={{ page_number }}">{{ page_number }}</a>
{% endif %}
{% endfor %}
첫번 째
if
문은 현재 반복중인 페이지가 현재 페이지와 일치하면 클릭해도 반응없는<a>
태그를 생성하기 위함이다.
나머지
elif
문은 현재 페이지에 따라서총 5개의 인접한 페이지만 호출
되도록 제한하는 내용이다.
그 외 중앙정렬, 폰트, 호버 등 간단한 css를 적용하기 위해
<a>
태그별로클래스
를 지정했다.
페이지네이션은 직접 완벽하게 만들기에는 많이 복잡하다. 웬만하면 남이 작성한 코드를 사용하자.