批次運算元據、自定義分頁器

2022-09-08 21:00:52

批次運算元據

瀏覽器存取一個django路由 立刻建立10萬條資料並展示到前端頁面

1.常規批次插入資料

def index(request):
    # 插入10000條資料
    for i in range(10000):
        models.Book.objects.create(title='第%s本書' % i)
    book_queryset = models.Book.objects.all()

2.使用orm提供的bulk_create方法批次插入資料(效率高 減少操作時間)

def index(request):
    # 插入10000條資料
    # for i in range(10000):
    #     models.Book.objects.create(title='第%s本書' % i)
    book_list = []
    for i in range(10000):
        book_obj = models.Book(title=f'第{i}本書')
        book_list.append(book_obj)
    # [models.Book(title=f'第{i}本書') for i in range(10000)]  # 可以使用列表生成式
    models.Book.objects.bulk_create(book_list)  # 批次建立
    # models.Book.objects.bulk_update()  # 批次建立
    book_queryset = models.Book.objects.all()  # 查詢資料
    return render(request, 'bookList.html', locals())
涉及到大批次資料的建立 直接使用create可能會造成資料庫崩潰
    批次資料建立>>>:bulk_create()
    批次資料修改>>>:bulk_update()

前端頁面展示資料

{% for book_obj in book_queryset %}
    <p>{{ book_obj.title }}</p>
{% endfor %}

自定義分頁器

1.針對上一小節批次插入的資料,我們在前端展示的時候發現一個很嚴重的問題,一頁展示了索引的資料,資料量太大,檢視不方便
2.針對資料量大但又需要全部展示給使用者觀看的情況下,我們統一做法都是做分頁處理。很多網站都做了這樣的操作

分頁推導過程

當資料量比較大的時候 頁面展示應該考慮分頁
1.QuerySet切片操作
2.分頁樣式新增
3.頁碼展示
	如何根據總資料和每頁展示的資料得出總頁碼
   	divmod()
4.如何渲染出所有的頁碼標籤
	前端模板語法不支援range 但是後端支援 我們可以在後端建立好html標籤然後傳遞給html頁面使用
5.如何限制住展示的頁面標籤個數
	頁碼推薦使用奇數位(對稱美)  利用當前頁前後固定位數來限制
6.首尾頁碼展示範圍問題
"""
上述是分頁器元件的推導流程 我們無需真正編寫
	django自帶一個分頁器元件 但是不太好用 我們自己也寫了一個
"""

1.依靠索引切片

def ab_pl(request):
   book_data = models.Book.objects.all()
    # 計算總共資料條數
    all_count = book_data.count()
    # 2.自定義每頁展示的資料條數
    per_page_num = 10
    all_page_num, more = divmod(all_count, per_page_num)
    if more:
        all_page_num += 1
    # 後端生成頁碼標籤
    html_page = ''
    for i in range(1, all_page_num):
        html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)

    # 1.獲取前端想要展示的頁碼
    current_page = request.GET.get('page', 1)  # 獲取使用者展示的page頁 如果麼有則預設展示1
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    # 3.定義出切片起始位置
    start_num = (current_page - 1) * per_page_num
    # 4.定義出切片終止位置
    end_num = current_page * per_page_num
    book_query = book_data[start_num:end_num]  # QuerySet [資料物件 資料物件]
    return render(request, 'bookList.html', locals())

動態計算/解析出 起始位置 與 終止位置

# 每頁展示10條
per_page_num = 10
    頁                    起始位置             終止位置  
current_page            start_page          end_page
    1                       0                   10 
    2                       10                  20 
    3                       20                  30   
    4                       30                  40
    
# 每頁展示5條    
per_page_num = 5
    頁                    起始位置             終止位置  
current_page            start_page          end_page
    1                        0                  5
    2                        5                  10
    3                        10                 15
    4                        15                 20


計算出 起始位置 與 終止位置
        0  = (1 - 1) * 5       
start_page = (current_page - 1) * per_page_num

        5  =  1 * 5
end_page = current_page * per_page_num

2.通過程式碼動態計算出到底需要多少頁

使用內建方法divmod

內建方法之divmod
>>> divmod(100,10)
(10, 0)  # 10頁
>>> divmod(101,10)
(10, 1)  # 11頁
>>> divmod(99,10)
(9, 9)  # 10頁
# 餘數只要不是0就需要在第一個數位上加一

顯示每頁資料

book_data = models.Book.objects.all()
# 計算總共資料條數
all_count = book_data.count()
# 2.自定義每頁展示的資料條數
per_page_num = 10
all_page_num, more = divmod(all_count, per_page_num)
# 我們可以判斷元祖的第二個數位是否為0從而確定到底需要多少頁來展示資料
if more:
    all_page_num += 1
# 後端生成頁碼標籤
html_page = ''
for i in range(1, all_page_num):
    html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)

利用start_page和end_page對總資料進行切片取值再傳入前端頁面

book_query = book_data[start_num:end_num]  # QuerySet [資料物件 資料物件]
    return render(request, 'bookList.html', locals())

copy分頁器的程式碼(bootstrap)

後端程式碼

def index(request):
    book_data = models.Book.objects.all()
    # 計算總共資料條數
    all_count = book_data.count()
    # 2.自定義每頁展示的資料條數
    per_page_num = 10
    all_page_num, more = divmod(all_count, per_page_num)
    if more:
        all_page_num += 1
    # 後端生成頁碼標籤
    html_page = ''
    for i in range(1, all_page_num):
        html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)

    # 1.獲取前端想要展示的頁碼
    current_page = request.GET.get('page', 1)  # 獲取使用者展示的page頁 如果麼有則預設展示1
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    # 3.定義出切片起始位置
    start_num = (current_page - 1) * per_page_num
    # 4.定義出切片終止位置
    end_num = current_page * per_page_num
    book_query = book_data[start_num:end_num]  # QuerySet [資料物件 資料物件]
    return render(request, 'bookList.html', locals())

前端頁面

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            {% for book_obj in book_query %}
                <p class="text-center">{{ book_obj.title }}</p>
            {% endfor %}
            <nav aria-label="Page navigation " class="text-center">
                <ul class="pagination">
                    <li>
                        <a href="#" aria-label="Previous">
                            <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                    {{ html_page | safe }}
                    <li>
                        <a href="#" aria-label="Next">
                            <span aria-hidden="true">&raquo;</span>
                        </a>
                    </li>
                </ul>
            </nav>
        </div>
    </div>
</div>

限制展示頁面標籤個數

頁碼推薦使用奇數位(對稱美) 利用當前頁前後固定位數來限制

def index(request):
    book_data = models.Book.objects.all()
    # 計算總共資料條數
    all_count = book_data.count()
    # 2.自定義每頁展示的資料條數
    per_page_num = 10
    all_page_num, more = divmod(all_count, per_page_num)
    if more:
        all_page_num += 1
        # 1.獲取前端想要展示的頁碼
    current_page = request.GET.get('page', 1)  # 獲取使用者展示的page頁 如果麼有則預設展示1
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    # 後端生成頁碼標籤
    html_page = ''
    xxx = current_page
    # 一旦頁碼小於6的時候就叫他等於6,否則不做處理的話就會頁面變為負數,出現報錯
    if current_page < 6:
        xxx = 6
    for i in range(xxx - 5, xxx + 6):
        if current_page == i:
            # 如果當前頁面是展示的頁面就新增一個active
            html_page += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i)
        else:
            html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)
    # 3.定義出切片起始位置
    start_num = (current_page - 1) * per_page_num
    # 4.定義出切片終止位置
    end_num = current_page * per_page_num
    book_query = book_data[start_num:end_num]  # QuerySet [資料物件 資料物件]
    return render(request, 'bookList.html', locals())
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            {% for book_obj in book_query %}
                <p class="text-center">{{ book_obj.title }}</p>
            {% endfor %}
            <nav aria-label="Page navigation " class="text-center">
                <ul class="pagination">
                    <li>
                        <a href="#" aria-label="Previous">
                            <span aria-hidden="true">&laquo;</span>
                        </a>
                    </li>
                    {{ html_page |safe}}
                    <li>
                        <a href="#" aria-label="Next">
                            <span aria-hidden="true">&raquo;</span>
                        </a>
                    </li>
                </ul>
            </nav>
        </div>
    </div>
</div>

自定義分頁器使用

點選檢視程式碼
class Pagination(object):
    def __init__(self,current_page,all_count,per_page_num=2,pager_count=11):
        """
        封裝分頁相關資料
        :param current_page: 當前頁
        :param all_count:    資料庫中的資料總條數
        :param per_page_num: 每頁顯示的資料條數
        :param pager_count:  最多顯示的頁碼個數
        
        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        獲取資料用page_data而不再使用原始的queryset
        獲取前端分頁樣式用page_obj.page_html
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page <1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num


        # 總頁碼
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果總頁碼 < 11個:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 總頁碼  > 11
        else:
            # 當前頁如果<=頁面上最多顯示11/2個頁碼
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 當前頁大於5
            else:
                # 頁碼翻到最後
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 新增前面的nav和ul標籤
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首頁</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部新增標籤
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

自定義分頁器的拷貝及使用

1.當我們需要使用到非django內建的第三方功能或者元件程式碼的時候
我們一般情況下會建立一個名為utils資料夾 在該資料夾內對模組進行功能性劃分
utils可以在每個應用下建立 具體結合實際情況

2.我們到了後期封裝程式碼的時候 不再侷限於函數
還是儘量朝物件導向去封裝

3.我們自定義的分頁器是基於bootstrap樣式來的 所以你需要提前匯入bootstrap
	bootstrap   版本  v3	
	jQuery		版本 v3

最後封裝好的版本使用

點選檢視程式碼
"""
utils可以建在專案下,也可以建在應用下!
"""
class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
        """
        封裝分頁相關資料
        :param current_page: 當前頁
        :param all_count:    資料庫中的資料總條數
        :param per_page_num: 每頁顯示的資料條數
        :param pager_count:  最多顯示的頁碼個數
        """
        try:  # 轉換當前頁
            current_page = int(current_page)
        except Exception as e:
            current_page = 1
        # 保證當前頁被我們正常的獲取到
        if current_page < 1:
            current_page = 1

        # 物件賦值屬性
        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 總頁碼
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager
        # 賦值屬性
        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    # 方法偽裝成資料 物件調的時候不需要加括號 也可以正常存取到
    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):  # 生成分頁器所有編碼
        # 如果總頁碼 < 11個:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 總頁碼  > 11
        else:
            # 當前頁如果<=頁面上最多顯示11/2個頁碼
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 當前頁大於5
            else:
                # 頁碼翻到最後
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 新增前面的nav和ul標籤
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首頁</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一頁</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一頁</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):  # 高亮顯示
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一頁</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一頁</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾頁</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部新增標籤
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)


後端

# 匯入utils檔案
from utils.mypage import Pagination

def ab_pl(request):
    # 獲取頁面展示多少資料
    book_queryset = models.Book.objects.all()
    # 當前頁
    current_page = request.GET.get('page', 1)
    # 總條數
    all_count = book_queryset.count()
    # 1.傳值生成物件
    page_obj = Pagination(current_page=current_page, all_count=all_count)
    # 2.直接對總資料進行切片操作
    page_queryset = book_queryset[page_obj.start:page_obj.end]  # 計算出起始位置與終止位置
    # 3.將page_queryset傳遞到頁面 替換之前的book_queryset

    return render(request,'ab_pl.html',locals())

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--bootstrap引入 CSS CDN-->
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <!--jQuery引入 CDN-->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!--Bootstrap引入 Js CDN-->
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>

{#迴圈資料#}
{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>
{% endfor %}

{# 利用自定義分頁器直接顯示分頁器樣式 #}
{{ page_obj.page_html|safe }}

</body>
</html>