Melakukan Pagination dengan Django

Bagus Aji Santoso 6 Februari 2018

Melakukan Pagination dengan Django

Diterjemahkan dari How to Paginate with Django karya Vitor Freitas.

Level menengah. Pembaca diasumsikan sudah pernah membuat aplikasi web dengan Django.

Pagination menjadi menjadi proses yang sering dilakukan oleh situs-situs yang menampilkan banyak data. Dengan melakukan pagination, kita bisa membagi banyak data menjadi beberapa halaman terpisah. Tutorial ini akan menunjukkan bagaimana melakukan pagination menggunakan function based views maupuan class-based views (ListView).

Membangun Aplikasi Mini HRD dengan Django (6): Menambahkan Pagination

Kelas Paginator

Kelas paginator ada di django.core.paginator. Kita akan sering menggunakan kelas Paginator dan Page.

Anggap tabel auth.User memiliki 53 user instances.

from django.contrib.auth.models import User
from django.core.paginator import Paginator

user_list = User.objects.all()
paginator = Paginator(user_list, 10)

Pada contoh di atas, penulis memberitahu Paginator untuk melakukan pagination terhadap QuerySet user_list yang terdiri dari 10 user per halaman. Perintah di atas akan menghasilkan 6 halaman. Ke 5 halaman yang pertama berisi 10 User sedangkan halaman terakhir berisi 3 User.

Debugging Objek Paginator
InputOutputType
paginator.count53<type 'int'>
paginator.num_pages6<type 'int'>
paginator.page_rangexrange(1, 7)<type 'xrange'>
paginator.page(2)<Page 2 of 6><class 'django.core.paginator.Page'>

Method Paginator.page() akan mengembalikan halaman berdasarkan hasil pagination yang dilakukan dan menjadi objek bertipe Page. Objek itulah yang akan kita kirim ke template.

users = paginator.page(2)
Debugging Objek Page
InputOutputType
users<Page 2 of 6><class 'django.core.paginator.Page'>
users.has_next()True<type 'bool'>
users.has_previous()True<type 'bool'>
users.has_other_pages()True<type 'bool'>
users.next_page_number()3<type 'int'>
users.previous_page_number()1<type 'int'>
users.start_index()11<type 'int'>
users.end_index()20<type 'int'>

Method Page.next_page_number() dan Page.previous_page_number() dapat memanggil InvalidPage jika halaman next/previous tidak ada.

Method Page.start_index() dan Page.end_index() relatif terhadap nomor halaman.

>>> users = paginator.page(6)  # last page
<Page 6 of 6>
>>> users.start_index()
51
>>> users.end_index()
53

Proses ini terjadi dengan melakukan kueri ke database lalu mengirimkan QuerySet ke Paginator, mengambil sebuah objek Page dari Paginator dan mengirim objek Page ke template.

Mari lihat bagaimana penggunaannya praktisnya.

Pagination dengan Function-Based Views

views.py

from django.contrib.auth.models import User
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

def index(request):
    user_list = User.objects.all()
    page = request.GET.get('page', 1)

    paginator = Paginator(user_list, 10)
    try:
        users = paginator.page(page)
    except PageNotAnInteger:
        users = paginator.page(1)
    except EmptyPage:
        users = paginator.page(paginator.num_pages)

    return render(request, 'core/user_list.html', { 'users': users })

user_list.html

<table class="table table-bordered">
  <thead>
    <tr>
      <th>Username</th>
      <th>First name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    {% for user in users %}
      <tr>
        <td>{{ user.username }}</td>
        <td>{{ user.first_name }}</td>
        <td>{{ user.email }}</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

{% if users.has_other_pages %}
  <ul class="pagination">
    {% if users.has_previous %}
      <li><a href="?page={{ users.previous_page_number }}">«</a></li>
    {% else %}
      <li class="disabled"><span>«</span></li>
    {% endif %}
    {% for i in users.paginator.page_range %}
      {% if users.number == i %}
        <li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
      {% else %}
        <li><a href="?page={{ i }}">{{ i }}</a></li>
      {% endif %}
    {% endfor %}
    {% if users.has_next %}
      <li><a href="?page={{ users.next_page_number }}">»</a></li>
    {% else %}
      <li class="disabled"><span>»</span></li>
    {% endif %}
  </ul>
{% endif %}

Akan menghasilkan sesuatu seperti ini:

Pagination

Contoh di atas menggunakan Bootstrap 3.

Pagination dengan Class-Based Views

views.py

class UserListView(ListView):
    model = User
    template_name = 'core/user_list.html'  # Default: <app_label>/<model_name>_list.html
    context_object_name = 'users'  # Default: object_list
    paginate_by = 10
    queryset = User.objects.all()  # Default: Model.objects.all()

user_list.html

<table class="table table-bordered">
  <thead>
    <tr>
      <th>Username</th>
      <th>First name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    {% for user in users %}
      <tr>
        <td>{{ user.username }}</td>
        <td>{{ user.first_name }}</td>
        <td>{{ user.email }}</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

{% if is_paginated %}
  <ul class="pagination">
    {% if page_obj.has_previous %}
      <li><a href="?page={{ page_obj.previous_page_number }}">«</a></li>
    {% else %}
      <li class="disabled"><span>«</span></li>
    {% endif %}
    {% for i in paginator.page_range %}
      {% if page_obj.number == i %}
        <li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
      {% else %}
        <li><a href="?page={{ i }}">{{ i }}</a></li>
      {% endif %}
    {% endfor %}
    {% if page_obj.has_next %}
      <li><a href="?page={{ page_obj.next_page_number }}">»</a></li>
    {% else %}
      <li class="disabled"><span>»</span></li>
    {% endif %}
  </ul>
{% endif %}