Django에서는 pagination를 위한 다양한 기능들을이 기본적으로 구현돼있다. 그래서 간단한 설정만으로도 pagination을 쉽게 구현할 수 있다. 그리고 이 설정에 대한 부분은 문서에 자세히 정리되어 있다.
나는 이 pagination 기능을 이용하여 한 페이지당 10개의 객체들을 보여주는 Class-based view를 아래와 같이 구현하였다.
# students/views/list.py
from django.views.generic import ListView
from students.models import Student
class StudentListView(ListView):
model = Student
template_name = "students/list.html"
context_object_name = 'students'
paginate_by = 10 # Display 10 objects per page
여기서 문제는 객체가 많아질수록 아래의 페이지 숫자들이 계속 늘어난다는 것이었다. 즉, 객체가 총 500개라고 한다면, 아래에 페이지 숫자들은 Prev 1 2 ... 99 500 Next
, 총 50개가 나온다는 의미이다.
하지만 나는 아래의 페이지 숫자들을 항상 5개만 보여주고 싶었다. 예를 들어 1 ~ 5 페이지일 때는 아래의 페이지 숫자가 Prev 1 2 3 4 5 Next
로 나오고, 6 ~ 10 페이지일 때는 Prev 6 7 8 9 10 Next
로 나오게 하고 싶었다.
그러나 안타깝게도 Django에서는 페이지 숫자 범위를 내 입맛에 맞게 설정하는 기능은 제공해주지 않는 듯 했고(어쩌면 내가 못 찾은 것일 수도 모르지만), 어쩔 수 없이 아래와 같이 직접 구현하였다.
# students/views/list.py
from django.views.generic import ListView
from django.core.paginator import Paginator
from students.models import Student
class StudentListView(ListView):
model = Student
template_name = "students/list.html"
context_object_name = 'students'
paginate_by = 10 # Display 10 objects per page
def get_context_data(self, **kwargs):
context = super(StudentListView, self).get_context_data(**kwargs)
paginator = context['paginator']
page_numbers_range = 5 # Display only 5 page numbers
max_index = len(paginator.page_range)
page = self.request.GET.get('page')
current_page = int(page) if page else 1
start_index = int((current_page - 1) / page_numbers_range) * page_numbers_range
end_index = start_index + page_numbers_range
if end_index >= max_index:
end_index = max_index
page_range = paginator.page_range[start_index:end_index]
context['page_range'] = page_range
return context
get_context_data(self, **kwargs)
함수를 override하여 구현하였다. 그리고 현재 페이지에서 내가 원하는 page_range
를 template에 context로 넘겨주었다.
아래는 pagination과 관련된 전체 template 코드이다.
<!-- templates/students/list.html -->
{% for student in students %}
<!-- Student objects -->
{% endfor %}
{# Pagination #}
{% if is_paginated %}
<nav>
<ul class="pagination">
{% if page_obj.has_previous %}
<li>
<a href="?page={{ page_obj.previous_page_number }}">
<span>Prev</span>
</a>
</li>
{% else %}
<li class="disabled">
<a href="#">
<span>Previous</span>
</a>
</li>
{% endif %}
{% for page in page_range %}
<li {% if page == page_obj.number %}class="active"{% endif %}>
<a href="?page={{ page }}">{{ page }}</a>
</li>
{% endfor %}
{% if page_obj.has_next %}
<li>
<a href="?page={{ page_obj.next_page_number }}">
<span>Next</span>
</a>
</li>
{% else %}
<li {% if not page_obj.has_next %}class="disabled"{% endif %}>
<a href="#">
<span>Next</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
여기서 주목해야할 부분은 이 부분이다.
{% for page in page_range %}
<li {% if page == page_obj.number %}class="active"{% endif %}>
<a href="?page={{ page }}">{{ page }}</a>
</li>
{% endfor %}
원래 기존에 사용했던 {% for page in paginator.page_range %}
장고 템플릿 태그를 view에서 넘겨받은 context를 이용해 {% for page in page_range %}
로 바꿔주었다.