+1

Pagination in Django

Django cung cấp một vài class hỗ trợ bạn trong việc quản lý dữ liệu được phân trang. Điều đó có nghĩa là, dữ liệu sẽ được tách thành nhiều trang cùng với các link "Trước / Sau" ("Previous / Next" link). Bạn có thể xem mô tả của các class này ở file django/core/paginator.py.

NOTE: Chú ý, bài viết này được translate lại nhưng có chỉnh sửa cho phù hợp với môi trường Python 2.7.11 và Django 1.10.1

I. Example

Hãy truyền vào một list các object cùng với một số nguyên thể hiện số lượng item bạn muốn có ở mỗi page (trang) khi khởi tạo một object Paginator, nó sẽ trả về cho bạn một đối tượng cùng với các method để truy cập các item ở mỗi page:


from django.core.paginator import Paginator
objects = ['john', 'paul', 'george', 'ringo']
p = Paginator(objects, 2)

p.count
=> 4
p.num_pages
=> 2
type(p.page_range)
=> <class 'xrange'>
p.page_range
=> xrange(1, 3)

page1 = p.page(1)
page1
=> <Page 1 of 2>
page1.object_list
=> ['john', 'paul']

page2 = p.page(2)
page2.object_list
=> ['george', 'ringo']
page2.has_next()
=> False
page2.has_previous()
=> True
page2.has_other_pages()
=> True
page2.next_page_number()
=> Traceback (most recent call last):
   ...
   django.core.paginator.EmptyPage: That page contains no results
page2.previous_page_number()
=> 1
page2.start_index() # 1-based index của phần tử đầu tiên ở trang này
=> 3
page2.end_index() # 1-based index của phần tử cuối cùng ở trang này
=> 4

p.page(0)
=> Traceback (most recent call last):
   ...
   django.core.paginator.EmptyPage: That page number is less than 1
p.page(3)
=> Traceback (most recent call last):
   ...
   django.core.paginator.EmptyPage: That page contains no results

  • Note

    Bạn có thể cung cấp cho Paginator một list / tuple, một QuerySet, hoặc bất cứ một object nào khác có method count() hoặc len(). Khi xác định số object chứa trong object được truyền, Paginator đầu tiên sẽ gọi method count(), sau đó là method len() nếu như object được truyền không có method count(). Điều này cho phép các object QuerySet sử dụng method hiệu quả hơn - count().

II. Using Paginator in a view

Đây là một ví dụ phức tạp hơn một chút, sử dụng Paginator trong view để phân trang một QuerySet. Ví dụ này giả sử chúng ta có model Contacts và nó đã được import sẵn.

Hàm ở view như sau:


from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request):
    contact_list = Contacts.objects.all()
    paginator = Paginator(contact_list, 25) # Show 25 contacts mỗi page

    page_number = request.GET.get("page_number")
    try:
        contacts = paginator.page(page_number)
    except PageNotAnInteger:
        # Nếu page_number không thuộc kiểu integer, trả về page đầu tiên
        contacts = paginator.page(1)
    except EmptyPage:
        # Nếu page không có item nào, trả về page cuối cùng
        contacts = paginator.page(paginator.num_pages)

    return render(request, "list.html", {"contacts": contacts})

Còn đây là template list.html:


{% for contact in contacts %}
    {# Mỗi "contact" là một object của class Contacts. #}
    {{ contact.full_name|upper }}<br />
    ...
{% endfor %}

<div class="pagination">
    <span class="step-links">
        {% if contacts.has_previous %}
            <a href="?page={{ contacts.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
        </span>

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

III. Paginator objects

Class Paginator có constructor sau:

class Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True)[source]

1. Required arguments

a. object_list

Một list, tuple, QuerySet hay một sliceable object khác có method count() hoặc len(). Để việc phân trang được nhất quán, QuerySet nên được order với một mệnh đề order_by() hoặc một ordering mặc định ở mức model.

  • Performance issues paginating large QuerySets

    Nếu bạn đang sử dụng một QuerySet với một lượng lớn các item, việc request page number cao có thể sẽ dẫn đến vấn đề về performance. Đơn giản là vì câu query LIMIT / OFFSET trả về cần phải tính số lượng các record OFFSET - việc này mất càng nhiều thời gian khi page number càng lớn.

b. per_page

Số lượng item lớn nhất ở mỗi page, không kể orphans (xem phần b bên dưới).

2. Optional arguments

a. orphans

Số lượng item nhỏ nhất xuất hiện ở page cuối, mặc định là 0. Sử dụng argument này khi bạn không muốn có quá ít item xuất hiện ở page cuối. Nếu page cuối có lượng item nhỏ hơn hoặc bằng orphans, các item đó sẽ được thêm vào page trước đó và page được thêm item này sẽ trở thành page cuối. Ví dụ, với 23 item, per_page=10orphans=3, chúng ta sẽ thu được hai page: page đầu tiên với 10 item và page thứ hai với 13 item.

b. allow_empty_firs_page

Cho phép hay không cho phép page đầu tiên được phép empty. Nếu allow_empty_firs_page=Falseobject_list là empty, exception EmptyPage sẽ được raise lên.

3. Methods

Paginator.page(number)[source]

  • Trả về một đối tượng Page cùng với index của nó
  • Raise lên exception InvalidPage nếu page number không tồn tại

4. Attributes

a. Paginator.count

Tổng số object được phân trang

b. Paginator.num_pages

Tổng số page sau khi thực hiện việc phân trang

c. Paginator.page_range

Một 1-based range iterator, ví dụ như xrange(1, 3)

IV. InvalidPage exceptions

1. Exception InvalidPage[source]

Đây là base class cho các exception khi page number được truyền vào là không hợp lệ.

Method Paginator.page() raise lên exception nếu page được yêu cầu là không hợp lệ (ví dụ như khi truyền vào một string) hoặc không chứa bất cứ một object nào. Thông thường thì ta chỉ cần bắt exception InvalidPage là đủ. Tuy nhiên, bạn cũng có thể bắt các exception PageNotAnIntegerEmptyPage.

2. Exception PageNotAnInteger[source]

Raise lên khi page() được truyền vào tham số không thuộc kiểu integer.

3. Exception EmptyPage[source]

Raise lên khi page() được truyền vào tham số hợp lệ nhưng lại không có object nào tồn tại ở page đó.

V. Page objects

Thường thì bạn sẽ không khởi tạo các object Page bằng tay - bạn sẽ thu được chúng nhờ method Paginator.page().

class Page(object_list, number, paginator)[source]

1. Methods

a. Page.has_next()[source]

Trả về True nếu tồn tại page kế tiếp

b. Page.has_previous()[source]

Trả về True nếu tồn tại page trước đó

c. Page.has_other_pages()[source]

Trả về True nếu tồn tại page kế tiếp hoặc page trước đó

d. Page.next_page_number()[source]

Trả về page number của page kế tiếp. Raise lên exception InvalidPage nếu page kế tiếp không tồn tại

e. Page.previous_page_number()[source]

Trả về page number của page trước đó. Raise lên exception InvalidPage nếu page trước đó không tồn tại

f. Page.start_index()[source]

Trả về 1-based index của object đầu tiên của page hiện tại

g. Page.end_index()[source]

Trả về 1-based index của object cuối cùng của page hiện tại

2. Attributes

a. Page.object_list

List các object của page hiện tại

b. Page.number

Page number của page hiện tại

c. Page.paginator

Paginator của page hiện tại


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí