python2.0_day19_前端分頁功能的實現


我們前面完成的客戶紀錄展示,只有4條,如果有上百條就不能在1頁中全部展示了,那樣是不人性化的.另外一次性取出來,數據量也比較大.
假如現在有95條數據,我們想實現一個每頁展示20條,那就分為5頁.假如我們實現了,那么前端每一次請求就需要給后台提供參數了.這個參數就是告訴views里的視圖函數我取第幾頁.
需求分析:
95條,每頁20條
第一次請求 返回20條,並且后端返回當前返回是第幾頁 ,所以第一次返回是1
點擊下一頁 1+1=2 ,把2傳給后端,后端拿到后在把第二頁的內容返回給前端,並且把當前返回的頁這里是2,返回給前端.
按照這個需求,我們自己寫,也是很容易實現的(這是對於老手),但是這個分頁功能屬於一個常用而且通用的功能,Django就提供了Paginator模塊來實現后台分頁的功能.
Django提供的是后台分頁的功能,前端要使用bootstrap中的分頁示例代碼
我們先看看Django中處理分頁的模塊都有哪些方法:
$python3.5 manage.py  shell
>>> from django.core.paginator import Paginator # 導入Paginator
>>> objects = ['john','paul','george','ringo']
>>> p = Paginator(objects,2)    # 生成一個分頁的實例,兩個參數(objects是列表,2代表的是每2個元素分成一頁.)
那我們來看看p這個實例有幾個方法
>>> p.count             # 查看有多少個元素
4
>>> p.num_pages         # 查看總共有幾頁
2
>>> type(p.page_range)
<class 'range'>
>>> p.page_range        # 當我們想循環每一頁時就需要用到這個. for num in p.page_range:page = p.page(1)
range(1, 3)
>>> page1 = p.page(1)   # p.page(num) 取第幾頁
>>> page1               # page1 顯示這事第幾頁
<Page 1 of 2>
>>> page1.object_list   # 顯示頁里面的元素,以列表的方式
['john', 'paul']
>>> p.object_list       # 顯示p實例里有多少個元素
['john', 'paul', 'george', 'ringo']

>>> page2 = p.page(2)   # 第二頁
>>> page2.object_list   # 查看第二頁有多少個元素
['george', 'ringo']
>>> page2.has_next()    # 查看當前頁是不是有下一頁,如果有返回True,如果沒有Flase
False
>>> page2.has_previous()    # 查看當前頁是不是有上一頁,如果有返回True,如果沒有返回False
True
>>> page2.has_other_pages() # 查看除了當前頁之外還有沒有其它頁,如果有返回True,如果沒有返回False
True
>>> page1.next_page_number() # 查看當前頁的下一頁的頁碼
2
>>> page2.next_page_number()    # 查看當前頁的下一頁的頁碼,如果沒有則報錯
Traceback (most recent call last):
......
django.core.paginator.EmptyPage: That page contains no results
>>> page2.previous_page_number()    # 查看當前頁的上一頁的頁碼.
1
>>> page1.previous_page_number()    # 查看當前頁的上一頁的頁碼,如果沒有則報錯
Traceback (most recent call last):
......
django.core.paginator.EmptyPage: That page number is less than 1

>>> page1.start_index() # 查看當前頁中,第一個元素在總列表的索引值
1
>>> page2.start_index() # 查看當前頁中,第一個元素在總列表的索引值
3
>>> page1.end_index()   # 查看當前頁中,最后一個元素在總列表的索引值
2
>>> page2.end_index()   # 查看當前頁中,最后一個元素在總列表的索引值
4
>>> p.page(0)   # 當所取頁超出p.page_range()范圍,就會報錯了
Traceback (most recent call last):
...
django.core.paginator.EmptyPage: That page number is less than 1

Django分頁的官網
https://docs.djangoproject.com/en/1.9/topics/pagination/

我們來看下后台中到底如何使用,我們從django中查看有詳細的示例代碼.按照這些示例代碼完全沒問題

我們在crm/views.py文件中的代碼如下:
 1 from django.shortcuts import render
 2 from crm import models
 3 from django.core.paginator import  import Paginator,EmptyPage,PageNotAnInteger # 兩個異常
 4 # Create your views here.
 5 
 6 def dashboard(request):
 7     return render(request,'crm/dashboard.html')
 8 def customers(request):
 9     customer_list = models.Customer.objects.all()
10     paginator = Paginator(customer_list,2) # 每頁顯示2條紀錄
11     page = request.GET.get('page') #獲取客戶端請求傳來的頁碼
12     try:
13         customer_list = paginator.page(page) # 返回用戶請求的頁碼對象
14     except PageNotAnInteger:   # 如果請求中的page不是數字,也就是為空的情況下
15         customer_list = paginator.page(1)
16     except EmptyPage:
17         # 如果請求的頁碼數超出paginator.page_range(),則返回paginator頁碼對象的最后一頁
18         customer_list = paginator.page(paginator.num_pages)
19 
20     return render(request,'crm/customers.html',{'customer_list':customer_list})
需要注意的是:通過costomer_list = paginator.page(數字)獲得的對象,是paginator分頁實例,但是當我們對這個對象進行for循環時,遍歷出來的還是里面的元素.
所以我們可以在html模版中代碼依然是直接對 custormer_list 進行for循環,我一開始還以為要用{% for custormer in customer_list.object_list %}呢,結果在官網的html示例代碼中是{% for custormer in customer_list %}
我試了下,兩個都可以使用

然后我們在看下官網上給我們指引的需要在html模版文件中需要做的改動:

更改templates/crm/customer.html文件
 1 from django.shortcuts import render
 2 from crm import models
 3 from django.core.paginator import  import Paginator,EmptyPage,PageNotAnInteger # 兩個異常
 4 # Create your views here.
 5 
 6 def dashboard(request):
 7     return render(request,'crm/dashboard.html')
 8 def customers(request):
 9     customer_list = models.Customer.objects.all()
10     paginator = Paginator(customer_list,2) # 每頁顯示2條紀錄
11 
12     page = request.GET.get('page') #獲取客戶端請求傳來的頁碼
13 
14     try:
15         customer_list = paginator.page(page) # 返回用戶請求的頁碼對象
16     except PageNotAnInteger:   # 如果請求中的page不是數字,也就是為空的情況下
17         customer_list = paginator.page(1)
18     except EmptyPage:
19         # 如果請求的頁碼數超出paginator.page_range(),則返回paginator頁碼對象的最后一頁
20         customer_list = paginator.page(paginator.num_pages)
21 
22     return render(request,'crm/customers.html',{'customer_list':customer_list})
customer.html
訪問http://127.0.0.1:8000/crm/customers,結果如圖:

以上我們完成了一個最簡單的分頁.接下來我們可以對分頁進行優化下.
我們打開百度,隨便搜索一個關鍵字


要實現這種只要在前端進行更改就行了,我們可以直接使用bootcss.com中找分頁的組件.

我們將代碼拷貝到我們的customer.html文件中,然后進行更改,最終代碼如下:
 1 {% extends 'base.html' %}
 2 {% block page-header %}
 3     Customers List
 4 {% endblock %}
 5 {% block page-content %}
 6     <table class="table table-hover">
 7         <thead>
 8             <tr>
 9                 <th>ID</th>
10                 <th>QQ</th>
11                 <th>姓名</th>
12                 <th>渠道</th>
13                 <th>咨詢課程</th>
14                 <th>課程類型</th>
15                 <th>客戶備注</th>
16                 <th>狀態</th>
17                 <th>課程顧問</th>
18                 <th>日期</th>
19             </tr>
20         </thead>
21         <tbody>
22         {% for coustomer in customer_list.object_list %}
23             <tr>
24                 <td>{{ coustomer.id }}</td>
25                 <td>{{ coustomer.qq }}</td>
26                 <td>{{ coustomer.name }}</td>
27                 <td>{{ coustomer.source }}</td>
28                 <td>{{ coustomer.course }}</td>
29                 <td>{{ coustomer.get_course_type_display }}</td>
30                 <td>{{ coustomer.consult_memo|truncatechars:10 }}</td>
31                 <td class ="{{ coustomer.status }}"> {{ coustomer.get_status_display }} </td>
32                 <td>{{ coustomer.consultant }}</td>
33                 <td>{{ coustomer.date }}</td>
34             </tr>
35         {% endfor %}
36 
37         </tbody>
38     </table>
39     <div class="pagination">
40 
41         <nav>
42             <ul class="pagination">
43                 <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
44                 <!--<li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li>-->
45                 <!--我們要想獲得所有的頁面是不是先要知道有多少頁,然后對頁碼進行循環
46                 這里需要注意:我們知道paginator有一個方法page_range,可以獲得range(1,總頁數+1)這個<class 'range'>,但是我們這里的customer_list只是一個頁碼實例,它怎么獲得range類型呢
47                 可以,可以使用customer_list.paginator.page_range這樣就獲得了range(1,總頁數+1)這個<class 'range'>,接下來就是對這個range進行循環就可以了.
48                 -->
49 
50                 {% for page_num in customer_list.paginator.page_range %}
51                     <li class="active"><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
52                 {% endfor %}
53             </ul>
54         </nav>
55     </div>
56 {% endblock%}
更改后的customer.html
我們瀏覽下http://127.0.0.1:8000/crm/customers/
結果如下:

我們看到圖中標簽都是藍色,這是不對的,是因為
<li class="active"><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
里的class = "active",所以我們應該寫一個if,如果當前循環頁的頁碼和當前頁的頁碼一樣時才active
那么怎么獲得當前頁的頁碼呢.custormer_list.number就能獲得
代碼如圖:

 

再次訪問http://127.0.0.1:8000/crm/customers/
如圖:

下面我們在代碼中加入判斷,讓"上一頁按鈕"和"下一頁按鈕"生效
代碼如下:
 1     <div class="pagination">
 2 
 3         <nav>
 4             <ul class="pagination">
 5                 {% if customer_list.has_previous %}
 6                     <li class=""><a href="?page={{customer_list.previous_page_number}}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
 7                 {% endif %}
 8                 <!--<li class="active"><a href="#">1 <span class="sr-only">(current)</span></a></li>-->
 9                 <!--我們要想獲得所有的頁面是不是先要知道有多少頁,然后對頁碼進行循環
10                 這里需要注意:我們知道paginator有一個方法page_range,可以獲得range(1,總頁數+1)這個<class 'range'>,但是我們這里的customer_list只是一個頁碼實例,它怎么獲得range類型呢
11                 可以,可以使用customer_list.paginator.page_range這樣就獲得了range(1,總頁數+1)這個<class 'range'>,接下來就是對這個range進行循環就可以了.
12                 -->
13                 {% for page_num in customer_list.paginator.page_range %}
14                     {% if page_num  == customer_list.number %}
15                         <li class="active"><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
16                     {% else %}
17                         <li class=""><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
18                     {% endif %}
19                 {% endfor %}
20 
21                 {% if customer_list.has_next %}
22                     <li class=""><a href="?page={{customer_list.next_page_number}}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
23                 {% endif %}
24 
25             </ul>
26         </nav>
27     </div>
這時候頁碼功能就差不多了.

我們接着優化,這里我們只有2頁內容,按照上面的代碼,你有100頁,它也會在頁碼中顯示出來,這么一來就不人性化了.
我們看到百度里,一般會顯示指定數量的頁碼標簽,並且選中的標簽永遠在最中間.
如圖

那么上圖的效果如何實現呢?
我們把視圖重新改下,改成每一條紀錄為一頁,那么就會有4頁,我們就設置成在前端顯示3個頁碼標簽.被選中的顯示在中間.
思路:
1.首先通過customer_list.num能知道當前頁的頁碼.
2.循環顯示頁碼的時候,判斷比當前頁碼少多少可顯示,多多少可顯示,否則不顯示
3.這里有一個聰明的做法,取循環的值-當前頁頁碼所得差的絕對值就可.問題這是前端代碼,沒有abs(2-1)取絕對值的語法.那用什么語法?
前端的template前端里沒有求絕對值的語法,怎么辦呢?可以自己寫,Django中允許自定義template的語法.接下來我們來看如何自定義前端語法.

自定義Django的template語法
即自定義template tags(自定義模版標簽)
https://docs.djangoproject.com/es/1.9/howto/custom-template-tags/
1.你要想寫自定義標簽,首先要在你的app目錄,我們這里是crm目錄下創建一個python包文件(目錄名稱必須是templatetags/),在這個目錄下創建你的自定義標簽.

2.自定義標簽的內容怎么寫呢?
我們先看下官網上提供的一個示例代碼,實現全大寫的代碼:
1     from django import template
2 
3     register = template.Library() # 生成一個注冊器
4 
5     @register.filter # 注冊到語法庫,過濾語法 ,就是把數據輸入進來,內部執行后把改變的結果在反回來
6     def alex_upper(value):
7         return value.upper()

前端模版想用這個自定義template 標簽,前端頁面得知道有這個,默認前端肯定不知道,所以你想在前端使用,首先要在前端導入一下:
在{% extends 'base.html'%}下面一行導入,因為如果先導入會被覆蓋.
緊接着是如何在代碼中使用.
比如我們在顯示name字段時,把英文字母全部大寫.


完成上面代碼,你以為成功了,我們訪問測試http://127.0.0.1:8000/crm/customers/

結果出錯了,為什么?因為要重啟,這里是Django中為數不多更改后的內容需要重啟的地方.
$ python3.5 manage.py runserver 127.0.0.1:8000
重啟后,我們看結果

至此簡單的創建一個自定義的tempate 標簽我們就已經會了,接下來我們就可以自定義取絕對值的自定義標簽了.
我們看到剛剛創建的自定義template tags中注冊是:@register.filter用的是filter,filter過濾語法的特點是接收一個參數,處理后返回處理結果
我們這里要取絕對值,就需要傳入兩個參數,一個是當前頁的頁碼,還有一個for循環的頁碼.這就要用到支持多個參數的語法了叫做@register.simple_tag
django 的@register.simple_tag可以寫的很復雜,我們先不關,先用它實現我們簡單的需求:
 1 from django import template
 2 
 3 register = template.Library()
 4 
 5 @register.filter
 6 def alex_upper(value):
 7     return value.upper()
 8 
 9 @register.simple_tag
10 def guess_page(current_page,loop_num):
11     offset = abs(current_page - loop_num)
12     return offset
注意了,之前我們調用自定義的filter注冊的方法是通過"|alex_upper"
而現在我們是通過simple_tag注冊的,調用的方法是:
    {% 函數名 參數1 參數2 %}
這里是:
    {% guess_page customer_list.number page_num %}
但是我們的問題來了,{% guess_page customer_list.num page_num %} 返回的是一個具體的數值,我們需要設置一個變量,接收函數調用后返回的值.
但是前端是不能創建變量的,那么如何解決呢?
我們看,guess_page返回的是字符串值,那么我們干脆就直接返回html代碼,那么前端就可以就直接寫這段代碼就可以了
{% guess_page customer_list.num page_num %}
那么我們就把 guess_page函數重新改一遍:
把前端代碼的內容刪掉:
    {% if page_num  == customer_list.number %}
        <li class="active"><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
    {% else %}
        <li class=""><a href="?page={{page_num}}">{{page_num}}<span class="sr-only">(current)</span></a></li>
    {% endif %}
然后把這段內容,放到guess_page函數里,但是要把內容用python實現.
@register.simple_tag
def guess_page(current_page,loop_num):
    offset = abs(current_page - loop_num)
    # 如果絕對值小於2,就返回page_ele
    if offset < 2:
        # 如果當前頁碼等於循環的頁碼,則class="active"
        if current_page == loop_num:
            page_ele = '''
            <li class="active"><a href="?page={{%s}}">{{%s}}<span class="sr-only">(current)</span></a></li>
            '''%(loop_num,loop_num)
        # 如果當前頁碼不等於循環的頁碼,則class=""
        else:
            page_ele = '''
            <li class=""><a href="?page={{%s}}">{{%s}}<span class="sr-only">(current)</span></a></li>
            '''%(loop_num,loop_num)
        return page_ele

我們訪問http://127.0.0.1:8000/crm/customers/查看結果如圖:

拿不能就返回字符串啊,當然Django中有處理的方法,用format_html()
具體代碼如下:
 1     from django import template
 2     from django.utils.html import format_html  # 引入format_html模塊
 3 
 4     register = template.Library()
 5 
 6     @register.filter
 7     def alex_upper(value):
 8         return value.upper()
 9 
10     # @register.simple_tag
11     # def guess_page(current_page,loop_num):
12     #     offset = abs(current_page - loop_num)
13     #     return offset
14     @register.simple_tag
15     def guess_page(current_page,loop_num):
16         offset = abs(int(current_page) - int(loop_num))
17         # 如果絕對值
18         if offset < 2:
19             if current_page == loop_num:
20                 page_ele = '''
21                 <li class="active"><a href="?page=%s">%s<span class="sr-only">(current)</span></a></li>
22                 '''%(loop_num,loop_num)
23             else:
24                 page_ele = '''
25                 <li class=""><a href="?page=%s">%s<span class="sr-only">(current)</span></a></li>
26                 '''%(loop_num,loop_num)
27             return format_html(page_ele)

 


訪問http://127.0.0.1:8000/crm/customers/,查看結果

怎么處理這個None,簡單在if offset < 2:不滿足時返回空字符串
代碼如下
 1     @register.simple_tag
 2     def guess_page(current_page,loop_num):
 3         offset = abs(int(current_page) - int(loop_num))
 4         # 如果絕對值
 5         if offset < 2:
 6             if current_page == loop_num:
 7                 page_ele = '''
 8                 <li class="active"><a href="?page=%s">%s<span class="sr-only">(current)</span></a></li>
 9                 '''%(loop_num,loop_num)
10             else:
11                 page_ele = '''
12                 <li class=""><a href="?page=%s">%s<span class="sr-only">(current)</span></a></li>
13                 '''%(loop_num,loop_num)
14             return format_html(page_ele)
15         else:
16             return ''
我們在訪問http://127.0.0.1:8000/crm/customers/
結果如圖:

至此我們就實現了在Django框架結合bootstrap實現分頁的功能.看似簡單的一個功能,有那么知識!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM