본문 바로가기

개발/Web 개발

[Django] Paginator를 이용한 글 목록에 페이징 기능 적용 시키기

Django에서 제공하는 Paginator를 이용하여 게시판이나 리스트를 조회하는 모듈에 페이징을 적용 시킬수 있습니다.

 

페이징을 여러 view에 편리하게 적용시키기 위해 lib.py를 통해 PaginatorManager라는 함수를 정의하였습니다.

 

Paginator를 이용한 PaginatorManager 함수 구현

 

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

def PaginatorManager(request, queryset):
    page = request.GET.get('page', 1)

    paginator = Paginator(queryset, 20)

    max_index = len(paginator.page_range)
    current_page = int(page) if page else 1
    page_numbers_range = 5  # Display only 5 page numbers

    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]
    print(start_index,end_index)
    try:
        queryset = paginator.page(page)
    except PageNotAnInteger:
        queryset = paginator.page(1)
    except EmptyPage:
        queryset = paginator.page(paginator.num_pages)

    return page_range, queryset

 

정의된 함수는 request 객체와 queryset을 인자로 받습니다. 함수 첫째 줄에서 인자로 받은 request 객체의 'page' 값을 가져옵니다. 이때 값이 없다면 1을 return 받습니다. 다음 Paginator(queryset , 20) 함수를 통해 paginator 객체를 생성합니다. Paginator는 장고에서 제공하는 함수로 queryset과 페이지당 보여줄 객체의 수를 인자로 받습니다. 여기서는 페이지당 20개의 게시물을 보여주기 위해 20을 인자로 사용했습니다.

 

paginator.page_range를 통해 querryset과 게시물을 보여주기 위한 인자 값을 기반으로 page 범위를 return 받습니다. 그리고 max_index에 그 range 길이 값을 저장합니다.

 

current_page는 현재 보고있는 page입니다. current_page 변수에 page 변수가 존재하면 page를 정수형으로 형변환 합니다. 없다면 1을 current_page에 할당합니다. 또한 page_number_range 변수는 화면에 보여줄 page index의 수를 보여줍니다.

 

start_index와 end_index는 화면에 보여줄 index의 시작과 끝 값을 나타냅니다. current_page와 page_number_range를 통해 구해줍니다.

 

만약 end_index가 max_index보다 크다면 end_index는 max_index가 되어야 하기 때문에 그 값을 대입합니다.

 

다음, 화면에 보여줄 최대 5개의 index의 값을 지정하기 위해, page_range에 paginator.page_range[start_index:end_index]와 같이 처음과 끝 인덱스를 슬라이스로 지정해주어 page_range에 대입합니다.

 

try구문에서 paginator.page(page) 통해 인자로 받은 page에 해당하는 페이지의 object를 querryset을 변환하여 가져옵니다. paginator.get_page(page)도 사용이 가능한데, 어떤 차이점이 있는지는 잘 알지 못하겠습니다. 혹시 아시는 분이 있다면 댓글 부탁드립니다 :)

 

 

views.py 에서 사용하기

 

#views.py
from django.views.generic import ListView
from board.forms import BoardForm
from lib import PaginatorManager

class BoardList(ListView):
  def get_queryset(self):
          queryset = Board.objects.filter(active=1).all()
          return queryset

  def get_context_data(self, *, object_list=None, **kwargs):
          context = super(PersonnelInfo, self).get_context_data(**kwargs)
          context['page_range'], context['contacts'] = PaginatorManager(self.request, self.get_queryset())
          context['form'] = BoardForm()
          return context

 

PaginatorManger(self.request, self.get_queryset()) 을 통해 앞서 구현한 PaginatorManger 함수를 호출해줍니다. 그러면 return 값을 통해 화면에 보여줄 page_range와 querryset을 넘겨주고 이를 context를 통해 template로 넘겨줍니다.

 

 

template

 

<div class="row no-gutters d-flex justify-content-center">
        <ul class="pagination">
            {% if contacts.has_previous %}
            <li class="page-item">
                <a class="page-link" href="?page=1">&laquo; 처음</a>
            </li>
            <li class="page-item">
                <a class="page-link" href="?page={{ contacts.previous_page_number }}">이전</a>
            </li>
            {% else %}
            <li class="page-item disabled">
                <a class="page-link" href="#">&laquo; 처음</a>
            </li>
            <li class="page-item disabled">
                <a class="page-link" href="#">이전</a>
            </li>
            {% endif %}

            {% for page in page_range %}
            <li class="page-item {% if contacts.number == page %}active{% endif %}">
                <a class="page-link" href="?page={{ page }}">{{ page }}</a>
            </li>
            {% endfor %}

            {% if contacts.has_next %}
            <li class="page-item">
                <a class="page-link" href="?page={{ contacts.next_page_number }}">다음</a>
            </li>
            <li class="page-item">
                <a class="page-link" href="?page={{ contacts.paginator.num_pages }}">마지막 &raquo;</a>
            </li>
            {% else %}
            <li class="page-item disabled">
                <a class="page-link" href="#">다음</a>
            </li>
            <li class="page-item disabled">
                <a class="page-link" href="#">마지막 &raquo;</a>
            </li>
            {% endif %}
        </ul>
    </div>

 

views.py에서 넘겨준 context값을 통해 template의 페이징을 구현하였습니다.

 

bootstrap을 적용하였고, 다음과 같이 화면에 나타며 정상적으로 기능이 동작하는것을 확인할수 있었습니다.