1. 程式人生 > 實用技巧 >Django擴充套件 分頁元件

Django擴充套件 分頁元件

我們在展示資訊過程中,如果資訊量過多,會給人非常的不友好,這個時候需要通過分頁使得介面更簡潔友好

Django 基於GET請求的分頁功能

假設 每一個 顯示十條資料 可得到以下公式

頁碼起始結束
1 0 10
2 10 20
3 20 30
... ... ...
... (page-1)*5 page*10

我們用程式碼實現一下吧

page_num = int(request.GET.get("page")) # 獲取頁碼數

per_page_num = 10 # 每頁顯示條數

# 通過條件對查詢到的資料進行切片處理
customers_obj = models.Customer.objects.all()[
    (page_num
-1)*per_page_num:page_num*per_page_num ]

我們需要在前端生成分頁按鈕

分析

  • 需要獲取每頁顯示的數量
  • 需要獲取頁碼生成的數量
  • 前端進行迴圈生成

頁碼生成的數量 = 資料總數 / 每頁顯示的數量

def customers(request):

    page_num = int(request.GET.get("page"))
    per_page_num = 10  # 每頁顯示條數

    customers_count = models.Customer.objects.all().count() # 獲取總資料數量
    shanum, yunum = divmod(customers_count, per_page_num) #
計算商數和餘數 # 生成要展示的頁碼數 if yunum: page_num_count = shanum + 1 else: page_num_count = shanum # 根據每頁顯示的條數對查出來的資料進行切割 customers_obj = models.Customer.objects.all()[ (page_num-1)*per_page_num:page_num*per_page_num ] return render( request,
"saleshtml\\customers.html", { "customers_obj": customers_obj, "page_num_count": range(1, page_num_count+1) # 展示頁碼沒有第一頁 } )
<nav aria-label="Page navigation">
    <ul class="pagination">
      <li>
        <a href="#" aria-label="Previous">
          <span aria-hidden="true">&laquo;</span>
        </a>
      </li>
      {% for i in page_num_range %}
        <li><a href="/customers/?page={{i}}">{{i}}</a></li>
      {% endfor %}
      <li>
        <a href="#" aria-label="Next">
          <span aria-hidden="true">&raquo;</span>
        </a>
      </li>
    </ul>
  </nav>

  • 生成頁面樣式不友好 如果上百頁 就一堆頁碼條

對頁碼生成邏輯進行改進

所在頁碼欄位1欄位2欄位3欄位4欄位5
3 1 2 3 4 5
4 2 3 4 5 6
5 3 4 5 6 7
6 4 5 6 7 8

分析

  • 欄位三兩邊的數量: 分頁按鈕的條數 整除 2
  • 分頁按鈕起始: 當前頁碼 - 欄位三兩邊的數量
  • 分頁按鈕結束: 當前頁碼 + 欄位三兩邊的數量
def customers(request):
    try:
        page_num = int(request.GET.get("page"))
    except Exception:
        page_num = 1

    per_page_num = 10  # 每頁顯示條數

    customers_count = models.Customer.objects.all().count() # 獲取總資料數量
    shanum, yunum = divmod(customers_count, per_page_num) # 計算商數和餘數

    # 生成要展示的頁碼數
    if yunum:
        page_num_count = shanum + 1
    else:
        page_num_count = shanum

    # 如果頁碼數小於等於零 或者大於總頁碼數 讓他不出問題
    if page_num <= 0:
        page_num = 1
    elif page_num > page_num_count:
        page_num = page_num_count


    # 根據每頁顯示的條數對查出來的資料進行切割
    customers_obj = models.Customer.objects.all()[
        (page_num-1)*per_page_num:page_num*per_page_num
    ]

    page_num_show = 5 # 分頁按鈕生成的數量
    half_show = page_num_show//2 # 分頁按鈕中心兩端的數量
    start_page_num = page_num - half_show # 開始位置
    end_page_num = page_num + half_show +1 # 結束位置

    page_num_range = range(start_page_num, end_page_num)
    return render(
        request,
        "saleshtml\\customers.html",
        {
            "customers_obj": customers_obj,
            "page_num_range": page_num_range,
        }
    )

問題:

解決:

  • 頁碼數由後臺生成
start_page_num = page_num - half_show # 開始位置
end_page_num = page_num + half_show +1 # 結束位置
  • 解決問題
def customers(request):
    try:
        page_num = int(request.GET.get("page"))
    except Exception:
        page_num = 1

    per_page_num = 10  # 每頁顯示條數

    customers_count = models.Customer.objects.all().count() # 獲取總資料數量
    shanum, yunum = divmod(customers_count, per_page_num) # 計算商數和餘數

    # 生成要展示的頁碼數
    if yunum:
        page_num_count = shanum + 1
    else:
        page_num_count = shanum

    # 如果頁碼數小於等於零 或者大於總頁碼數 讓他不出問題
    if page_num <= 0:
        page_num = 1
    elif page_num > page_num_count:
        page_num = page_num_count


    # 根據每頁顯示的條數對查出來的資料進行切割
    customers_obj = models.Customer.objects.all()[
        (page_num-1)*per_page_num:page_num*per_page_num
    ]

    page_num_show = 5 # 分頁按鈕生成的數量
    half_show = page_num_show//2 # 分頁按鈕中心兩端的數量

    if page_num - half_show <= 0:
        start_page_num = 1
        end_page_num = page_num_show + 1
    elif page_num + half_show > page_num_count:
        start_page_num = page_num_count - page_num_show + 1
        end_page_num = page_num_count + 1
    else:
        start_page_num = page_num - half_show # 開始位置
        end_page_num = page_num + half_show +1 # 結束位置

    page_num_range = range(start_page_num, end_page_num)
    return render(
        request,
        "saleshtml\\customers.html",
        {
            "customers_obj": customers_obj,
            "page_num_range": page_num_range,
        }
    ) 

對分頁功能進行封裝

封裝到後臺

def customers(request):
    try:
        page_num = int(request.GET.get("page"))
    except Exception:
        page_num = 1

    per_page_num = 10  # 每頁顯示條數

    customers_count = models.Customer.objects.all().count() # 獲取總資料數量
    shanum, yunum = divmod(customers_count, per_page_num) # 計算商數和餘數

    # 生成要展示的頁碼數
    if yunum:
        page_num_count = shanum + 1
    else:
        page_num_count = shanum

    # 如果頁碼數小於等於零 或者大於總頁碼數 讓他不出問題
    if page_num <= 0:
        page_num = 1
    elif page_num > page_num_count:
        page_num = page_num_count


    # 根據每頁顯示的條數對查出來的資料進行切割
    customers_obj = models.Customer.objects.all()[
        (page_num-1)*per_page_num:page_num*per_page_num
    ]

    page_num_show = 5 # 分頁按鈕生成的數量
    half_show = page_num_show//2 # 分頁按鈕中心兩端的數量

    if page_num - half_show <= 0:
        start_page_num = 1
        end_page_num = page_num_show + 1
    elif page_num + half_show > page_num_count:
        start_page_num = page_num_count - page_num_show + 1
        end_page_num = page_num_count + 1
    else:
        start_page_num = page_num - half_show # 開始位置
        end_page_num = page_num + half_show +1 # 結束位置

    page_num_range = range(start_page_num, end_page_num)

    page_html = ''

    # 開頭標籤樣式之類
    page_per_html = '''
        <nav aria-label="Page navigation">
        <ul class="pagination">'''
    page_html += page_per_html

    # 上一頁
    if page_num > 1:
        per_page = f'''
            <li><a href="/customers/?page={page_num-1}" aria-label="Previous">
            <span aria-hidden="true">&laquo;</span></a></li>'''
        page_html += per_page

    # 迴圈新增按鈕
    for page in page_num_range:
        page_num_html = f'''
            <li><a href="/customers/?page={page}">{page}</a></li>
        '''
        page_html += page_num_html

    # 下一頁
    if page_num < page_num_count:
        next_page = f'''
        <li><a href="/customers/?page={page_num+1}" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
        </a></li>
        '''
        page_html += next_page

    # 標籤樣式收尾
    page_next_html = '''</ul></nav>'''
    page_html += page_next_html

    return render(
        request,
        "saleshtml\\customers.html",
        {"customers_obj": customers_obj, "page_html": page_html,})

<!--前端通過safe使字串變成可用標籤-->
{{ page_html|safe }} 

將分頁功能封裝成元件

# mark_safe 將後端生成的字串標記為HTML 前端直接使用 就不用再 {{xxx|salf}}
from django.utils.safestring import mark_safe

class MyPageNation():
    def __init__(self, page_num, total_count, per_page_num, page_num_show, base_url):
        self.per_page_num = per_page_num  # 每頁顯示資料條數
        self.page_num_show = page_num_show  # 分頁按鈕生成的數量
        self.base_url = base_url  # 分頁元件中的URL
        self.total_count = total_count # 資料的總量
        try:
            page_num = int(page_num)
        except Exception:
            page_num = 1
        self.page_num = page_num  # 請求的page
        shanum, yunum = divmod(self.total_count, self.per_page_num)  # 計算商數和餘數

        # 生成要展示的頁碼數
        if yunum:
            self.page_num_count = shanum + 1
        else:
            self.page_num_count = shanum

        # 如果頁碼數小於等於零 或者大於總頁碼數 讓他不出問題
        if self.page_num <= 0:
            self.page_num = 1
        elif page_num > self.page_num_count:
            self.page_num = self.page_num_count

        half_show = self.page_num_show//2  # 分頁按鈕中心兩端的數量

        if page_num - half_show <= 0:
            start_page_num = 1
            end_page_num = self.page_num_show + 1
        elif page_num + half_show > self.page_num_count:
            start_page_num = self.page_num_count - self.page_num_show + 1
            end_page_num = self.page_num_count + 1
        else:
            start_page_num = page_num - half_show
            end_page_num = page_num + half_show + 1
        self.start_page_num = start_page_num  # 開始位置
        self.end_page_num = end_page_num  # 結束位置

    @property
    def start_data_num(self):
        return (self.page_num-1)*self.per_page_num

    @property
    def end_data_num(self):
        return self.page_num*self.per_page_num

    def page_html(self):
        page_num_range = range(self.start_page_num, self.end_page_num)
        page_html = ''
        # 開頭標籤樣式之類
        page_per_html = '''
            <nav aria-label="Page navigation">
            <ul class="pagination">'''
        page_html += page_per_html
        # 上一頁
        if self.page_num > 1:
            per_page = f'''
                <li><a href="{self.base_url}?page={self.page_num-1}" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span></a></li>'''
            page_html += per_page
        # 迴圈新增按鈕
        for page in page_num_range:
            if self.page_num == page:
                page_num_html = f'''
                    <li class="active"><a href="{self.base_url}?page={page}">{page}</a></li>
                '''
            else:
                page_num_html = f'''
                    <li><a href="{self.base_url}?page={page}">{page}</a></li>
                '''
            page_html += page_num_html
        # 下一頁
        if self.page_num < self.page_num_count:
            next_page = f'''
            <li><a href="{self.base_url}?page={self.page_num+1}" aria-label="Next">
            <span aria-hidden="true">&raquo;</span>
            </a></li>
            '''
            page_html += next_page
        # 標籤樣式收尾
        page_next_html = '''</ul></nav>'''
        page_html += page_next_html
        
        return mark_safe(page_html) # 將最後生成的html標籤返回給前端