在上篇文章中,咱們僅僅是展現了最基礎的表格字段的內容,這篇文章咱們來添加過濾功能和分頁功能!html
1 In [189]: from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage 2 3 In [190]: Paginator? 4 Init signature: Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True) 5 Docstring: <no docstring> 6 File: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py 7 Type: type 8 9 In [191]: list1 = [1,2,3,4,5,6,7,8,9,10,11,12] 10 11 In [192]: pagination = Paginator(list1, 3) 12 13 In [193]: dir(pagination) 14 Out[193]: 15 ['__class__', 16 '__delattr__', 17 '__dict__', 18 '__dir__', 19 '__doc__', 20 '__eq__', 21 '__format__', 22 '__ge__', 23 '__getattribute__', 24 '__gt__', 25 '__hash__', 26 '__init__', 27 '__init_subclass__', 28 '__le__', 29 '__lt__', 30 '__module__', 31 '__ne__', 32 '__new__', 33 '__reduce__', 34 '__reduce_ex__', 35 '__repr__', 36 '__setattr__', 37 '__sizeof__', 38 '__str__', 39 '__subclasshook__', 40 '__weakref__', 41 '_check_object_list_is_ordered', 42 '_get_page', 43 'allow_empty_first_page', 44 'count', 45 'num_pages', 46 'object_list', 47 'orphans', 48 'page', 49 'page_range', 50 'per_page', 51 'validate_number'] 52 53 In [194]: [item for item in dir(pagination) if not item.startswith('_')] 54 Out[194]: 55 ['allow_empty_first_page', 56 'count', 57 'num_pages', 58 'object_list', 59 'orphans', 60 'page', 61 'page_range', 62 'per_page', 63 'validate_number'] 64 65 In [195]: pagination.count 66 Out[195]: 12 67 68 In [196]: pagination.num_pages 69 Out[196]: 4 70 In [198]: pagination.page(1) 71 Out[198]: <Page 1 of 4> 72 73 In [199]: pagination.page_range 74 Out[199]: range(1, 5) 75 76 In [200]: pagination.validate_number? 77 Signature: pagination.validate_number(number) 78 Docstring: Validates the given 1-based page number. 79 File: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py 80 Type: method 81 82 In [201]: pagination.validate_number(1) 83 Out[201]: 1 84 In [202]: pagination.validate_number(5) 85 --------------------------------------------------------------------------- 86 EmptyPage Traceback (most recent call last) 87 <ipython-input-202-b3575c7c07a1> in <module>() 88 ----> 1 pagination.validate_number(5) 89 90 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py in validate_number(self, number) 91 48 pass 92 49 else: 93 ---> 50 raise EmptyPage(_('That page contains no results')) 94 51 return number 95 52 96 97 EmptyPage: That page contains no results 98 99 In [203]: pagination.validate_number(4) 100 Out[203]: 4 101 In [205]: pagination.object_list 102 Out[205]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 103 104 In [210]: pagination.page(1).object_list 105 Out[210]: [1, 2, 3] 106 107 In [211]: pagination.page(2).object_list 108 Out[211]: [4, 5, 6] 109 110 In [212]: pagination.page(3).object_list 111 Out[212]: [7, 8, 9] 112 113 In [213]: pagination.page(4).object_list 114 Out[213]: [10, 11, 12] 115 116 In [227]: pagination.page(4).number 117 Out[227]: 4 118 119 In [228]: pagination.page(4).index(12) 120 Out[228]: 2 121 122 In [229]: pagination.page(4).object_list 123 Out[229]: [10, 11, 12] 124 125 In [230]: pagination.page(4).start_index() 126 Out[230]: 10 127 128 In [231]: pagination.page(4).end_index() 129 Out[231]: 12 130 131 In [232]: pagination.page(4).has_previous() 132 Out[232]: True 133 134 In [233]: pagination.page(4).has_next() 135 Out[233]: False 136 137 In [234]: pagination.page(2).next_page_number() 138 Out[234]: 3
在king_admin.py
文件中添加分頁字段,表示每頁顯示多少內容,以下:前端
1 ... 2 #建立基類 3 class BaseAdmin(object): 4 list_display = [] 5 list_filter = [] 6 list_per_page = 2 #添加此數據 7 ...
若是咱們須要修改每頁要顯示的內容數量時,一樣是在king_admin
中,只須要在自定義的子類中添加便可。以下:python
1 #自定義類,顯示特定字段 2 class CustomerAdmin(BaseAdmin): 3 list_display = ['qq','name','source','consultant','consult_course','date','status'] 4 list_filters = ['source','consultant','consult_course','status'] 5 list_per_page = 2 #添加此數據覆蓋基類數據
在Django
中,已經爲咱們提供了封裝好的分頁模塊,只須要引用便可!具體的使用看看官網的樣例就明白了。數據庫
啥也不說,直接上代碼:django
1 def display_objects(request, app_name, table_name): 2 #獲取自定義的admin_class 3 admin_class = enabled_admins[app_name][table_name] 4 #數據查詢 5 #query_set = admin_class.model.objects.all() 6 #分頁處理 7 #1.分頁對象參數構建:對象列表,每頁顯示數量 8 query_set_list = admin_class.model.objects.all() 9 10 #2.分頁對象建立 11 paginator = Paginator(query_set_list, admin_class.list_per_page) 12 #3.獲取前端點擊的頁面數值 13 get_page = request.GET.get('page') 14 #4.頁面異常處理 15 try: 16 #直接獲取該頁內容 17 query_set = paginator.page(get_page) 18 except PageNotAnInteger: 19 #不是整數值,跳轉到首頁 20 query_set = paginator.page(1) 21 except EmptyPage: 22 #超出範圍,跳轉到最後一頁 23 query_set = paginator.page(paginator.num_pages) 24 return render(request, 'king_admin/table_objs.html', 25 {'admin_class': admin_class, 26 'query_set': query_set})
In [189]: from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage In [190]: Paginator? Init signature: Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True) Docstring: <no docstring> File: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py Type: type In [191]: list1 = [1,2,3,4,5,6,7,8,9,10,11,12] In [192]: pagination = Paginator(list1, 3) In [193]: dir(pagination) Out[193]: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_check_object_list_is_ordered', '_get_page', 'allow_empty_first_page', 'count', 'num_pages', 'object_list', 'orphans', 'page', 'page_range', 'per_page', 'validate_number'] In [194]: [item for item in dir(pagination) if not item.startswith('_')] Out[194]: ['allow_empty_first_page', 'count', 'num_pages', 'object_list', 'orphans', 'page', 'page_range', 'per_page', 'validate_number'] In [195]: pagination.count # 數據總數 Out[195]: 12 In [196]: pagination.num_pages # 共分了多少頁 Out[196]: 4 In [198]: pagination.page(1) Out[198]: <Page 1 of 4> In [199]: pagination.page_range # 頁碼數列 Out[199]: range(1, 5) In [200]: pagination.validate_number? Signature: pagination.validate_number(number) Docstring: Validates the given 1-based page number. File: /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py Type: method In [201]: pagination.validate_number(1) Out[201]: 1 In [202]: pagination.validate_number(5) --------------------------------------------------------------------------- EmptyPage Traceback (most recent call last) <ipython-input-202-b3575c7c07a1> in <module>() ----> 1 pagination.validate_number(5) /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/core/paginator.py in validate_number(self, number) 48 pass 49 else: ---> 50 raise EmptyPage(_('That page contains no results')) 51 return number 52 EmptyPage: That page contains no results In [203]: pagination.validate_number(4) # 是由有頁碼4 Out[203]: 4 In [205]: pagination.object_list # 全部數據表項 Out[205]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] In [210]: pagination.page(1).object_list # 第一頁的全部數據 Out[210]: [1, 2, 3] In [211]: pagination.page(2).object_list # 第二頁的全部數據 Out[211]: [4, 5, 6] In [212]: pagination.page(3).object_list # 第三頁的全部數據 Out[212]: [7, 8, 9] In [213]: pagination.page(4).object_list # 第四頁的全部數據 Out[213]: [10, 11, 12]
# 分頁對象的經常使用方法 In [226]: pagination.page(4).index(12) # 數據12所在頁的位置 Out[226]: 2 In [227]: pagination.page(4).number # 頁的頁碼 Out[227]: 4 In [229]: pagination.page(4).object_list # 頁的全部數據 Out[229]: [10, 11, 12] In [230]: pagination.page(4).start_index() # 頁的開始項 Out[230]: 10 In [231]: pagination.page(4).end_index() # 頁的結束項 Out[231]: 12 In [232]: pagination.page(4).has_previous() # 頁是否有上一頁 Out[232]: True In [233]: pagination.page(4).has_next() # 頁是否有下一頁 Out[233]: False In [234]: pagination.page(2).next_page_number() # 頁的下一頁代碼 Out[234]: 3
此時,咱們須要對二級頁面添加一些內容,用於顯示分頁效果,以下:數據結構
1 {% extends 'king_admin/table_index.html' %} 2 {% load tags %} 3 {% block container %} 4 <div class="panel panel-info"> 5 <div class="panel-heading"> 6 <h3 class="panel-title">Panel title</h3> 7 </div> 8 <div class="panel-body"> 9 {#具體的表格內容展現 #} 10 <table class="table table-hover"> 11 <thead> 12 <tr> 13 {% for title_name in admin_class.list_display %} 14 <th>{{ title_name }}</th> 15 {% endfor %} 16 </tr> 17 </thead> 18 <tbody> 19 {% for item in query_set %} 20 <tr> 21 {#建立列表行數據#} 22 {% create_row item admin_class %} 23 </tr> 24 {% endfor %} 25 </tbody> 26 </table> 27 <nav> 28 {#分頁處理#} 29 <ul class="pagination"> 30 {#判斷是否有上一頁#} 31 {% if query_set.has_previous %} 32 <li class=""><a href="?page={{ query_set.previous_page_number }}">上一頁</a></li> 33 {% endif %} 34 {% create_page_element query_set %} 35 {#判斷是否有下一頁#} 36 {% if query_set.has_next %} 37 <li class=""><a href="?page={{ query_set.next_page_number }}">下一頁</a></li> 38 {% endif %} 39 </ul> 40 </nav> 41 </div> 42 </div> 43 {% endblock %}
標籤文件的內容:app
1 from django import template 2 from django.utils.safestring import mark_safe 3 register = template.Library() 4 #------------------------顯示錶名稱-->中文--------------------------- 5 @register.simple_tag 6 def render_verbose_name(admin_class): 7 return admin_class.model._meta.verbose_name 8 #-------------------------建立表格行數據----------------------------- 9 @register.simple_tag 10 def create_row(query_set_obj, admin_class): 11 #建立標籤元素--空,None不行 12 element = '' 13 #遍歷要顯示的models字段 14 for row in admin_class.list_display: 15 #獲取顯示字段對應的字段對象 16 field_obj = admin_class.model._meta.get_field(row) 17 #獲取數據 18 #判斷choice 19 if field_obj.choices: 20 #經過反射獲取對象裏面的值,並執行該方法get_字段_display()獲取choices裏面的數值 21 row_data = getattr(query_set_obj, 'get_{0}_display'.format(row))() 22 else: 23 row_data = getattr(query_set_obj, row) 24 #時間格式轉換 25 if type(row_data).__name__ == 'datetime': 26 row_data = row_data.strftime('%Y-%m-%d %H-%M-%S') 27 #標籤元素的拼接 28 element += "<td>{0}</td>".format(row_data) 29 return mark_safe(element) 30 #-----------------------------分頁處理--------------------------------- 31 @register.simple_tag 32 def create_page_element(query_set): 33 '''返回整個分頁元素''' 34 page_btns = '' 35 added_dot_ele = False #標誌符 36 for page_num in query_set.paginator.page_range: 37 if page_num < 3 or page_num > query_set.paginator.num_pages -2 \ 38 or abs(query_set.number - page_num) <= 2: #表明最前2頁或最後2頁 #abs判斷先後1頁 39 element_class = "" 40 if query_set.number == page_num: 41 added_dot_ele = False 42 element_class = "active" 43 page_btns += '''<li class="%s"><a href="?page=%s">%s</a></li>''' % (element_class, page_num, page_num) 44 else: #顯示... 45 if added_dot_ele == False: #如今還沒加... 46 page_btns += '<li><a>...</a></li>' 47 added_dot_ele = True 48 return mark_safe(page_btns)
渲染以後的效果:ssh
一樣是在king_admin.py
中添加過濾字段,能夠本身添加過濾條件,添加內容以下:函數
1 ... 2 #建立基類 3 class BaseAdmin(object): 4 list_display = [] 5 list_filter = [] #添加此數據 6 list_per_page = 2 7 ...
若是咱們須要修改每頁的過濾條件時,一樣是在king_admin中,只須要在自定義的子類中添加便可。以下:優化
1 #自定義類,顯示特定字段 2 class CustomerAdmin(BaseAdmin): 3 list_display = ['qq','name','source','consultant','consult_course','date','status'] 4 list_filters = ['source','consultant','consult_course','status'] #添加此數據覆蓋基類數據 5 list_per_page = 2
將過濾功能獨立出來,便於咱們對後面的添加的功能更好的結合與修改,在king_admin
應用目錄下建立utils.py
文件,並編寫過濾功能函數:
1 ----------------過濾功能------------------------------ 2 def table_filter(request, admin_class): 3 """條件過濾,並構造濾後的數據結構""" 4 filter_conditions = {} 5 for k, v in request.GET.items(): 6 if v: 7 filter_conditions[k] = v 8 return admin_class.model.objects.filter(**filter_conditions), filter_conditions
在基於分頁功能的基礎,添加過濾功能:
1 def display_objects(request, app_name, table_name): 2 #獲取自定義的admin_class 3 admin_class = enabled_admins[app_name][table_name] 4 #數據查詢 5 #query_set = admin_class.model.objects.all() 6 #分頁處理 7 #1.分頁對象參數構建:對象列表,每頁顯示數量 8 #query_set_list = admin_class.model.objects.all() 9 #延伸===>添加過濾條件 10 query_set_list, filter_conditions = table_filter(request, admin_class) 11 #2.分頁對象建立 12 paginator = Paginator(query_set_list, admin_class.list_per_page) 13 #3.獲取前端點擊的頁面數值 14 get_page = request.GET.get('page') 15 #4.頁面異常處理 16 try: 17 #直接獲取該頁內容 18 query_set = paginator.page(get_page) 19 except PageNotAnInteger: 20 #不是整數值,跳轉到首頁 21 query_set = paginator.page(1) 22 except EmptyPage: 23 #超出範圍,跳轉到最後一頁 24 query_set = paginator.page(paginator.num_pages) 25 return render(request, 'king_admin/table_objs.html', 26 {'admin_class': admin_class, 27 'query_set': query_set, 28 'filter_conditions': filter_conditions})
由於過濾的條件須要提交後臺,因此要構建表單,在table_ojs.html
文件中添加以下內容:
1 ... 2 <div class="panel-body"> 3 <div class="row"> 4 <form class="" method="get"> 5 {#條件過濾#} 6 {% for condition in admin_class.list_filters %} 7 <div class="col-lg-2"> 8 <span>{{ condition }}</span> 9 {% render_filter_element condition admin_class filter_conditions %} 10 </div> 11 {% endfor %} 12 <button type="SUBMIT" class="btn btn-success">檢索</button> 13 </form> 14 </div> 15 {#具體的表格內容展現 #} 16 <table class="table table-hover"> 17 ...
標籤文件內容:
1 from django import template 2 from django.utils.safestring import mark_safe 3 register = template.Library() 4 #------------------------顯示錶名稱-->中文--------------------------- 5 @register.simple_tag 6 def render_verbose_name(admin_class): 7 return admin_class.model._meta.verbose_name 8 #-------------------------建立表格行數據----------------------------- 9 @register.simple_tag 10 def create_row(query_set_obj, admin_class): 11 #建立標籤元素--空,None不行 12 element = '' 13 #遍歷要顯示的models字段 14 for row in admin_class.list_display: 15 #獲取顯示字段對應的字段對象 16 field_obj = admin_class.model._meta.get_field(row) 17 #獲取數據 18 #判斷choice 19 if field_obj.choices: 20 #經過反射獲取對象裏面的值,並執行該方法get_字段_display()獲取choices裏面的數值 21 row_data = getattr(query_set_obj, 'get_{0}_display'.format(row))() 22 else: 23 row_data = getattr(query_set_obj, row) 24 #時間格式轉換 25 if type(row_data).__name__ == 'datetime': 26 row_data = row_data.strftime('%Y-%m-%d %H-%M-%S') 27 #標籤元素的拼接 28 element += "<td>{0}</td>".format(row_data) 29 return mark_safe(element) 30 #-----------------------------分頁處理--------------------------------- 31 @register.simple_tag 32 def create_page_element(query_set): 33 '''返回整個分頁元素''' 34 page_btns = '' 35 added_dot_ele = False #標誌符 36 for page_num in query_set.paginator.page_range: 37 if page_num < 3 or page_num > query_set.paginator.num_pages -2 \ 38 or abs(query_set.number - page_num) <= 2: #表明最前2頁或最後2頁 #abs判斷先後1頁 39 element_class = "" 40 if query_set.number == page_num: 41 added_dot_ele = False 42 element_class = "active" 43 page_btns += '''<li class="%s"><a href="?page=%s">%s</a></li>''' % (element_class, page_num, page_num) 44 else: #顯示... 45 if added_dot_ele == False: #如今還沒加... 46 page_btns += '<li><a>...</a></li>' 47 added_dot_ele = True 48 return mark_safe(page_btns) 49 #--------------------------------過濾條件處理----------------------------- 50 @register.simple_tag 51 def render_filter_element(condition, admin_class, filter_conditions): 52 #初始化下拉框 53 select_element = """<select class='form-control' name={0}><option value=''>------ 54 </option>""".format(condition) 55 #獲取字段 56 field_object = admin_class.model._meta.get_field(condition) 57 #字段處理 58 # 默認不選中 59 selected = '' 60 #choice處理 61 if field_object.choices: 62 #遍歷choices值 63 for choice_item in field_object.get_choices()[1:]: 64 # print(choice_item) 65 #判斷選擇條件是否和choice值相等 66 if filter_conditions.get(condition) == str(choice_item[0]): 67 #被選中 68 selected = 'selected' 69 select_element += """<option value='{0}' {1}>{2}</option>""".format(choice_item[0], 70 selected, choice_item[1]) 71 selected = '' 72 #外鍵處理 73 if type(field_object).__name__ == 'ForeignKey': 74 for choice_item in field_object.get_choices()[1:]: 75 # 判斷選擇條件是否和choice值相等 76 if filter_conditions.get(condition) == str(choice_item[0]): 77 # 被選中 78 selected = 'selected' 79 select_element += """<option value='{0}' {1}>{2}</option>""".format(choice_item[0], 80 selected, choice_item[1]) 81 selected = '' 82 select_element += '</select>' 83 return mark_safe(select_element)
渲染後的結果:
搜索後對結果非常滿意,分頁顯示數量也是正確,然而,當點擊下一頁的時候確調回了初始狀態!這是爲何呢??
其實很簡單,咱們在作分頁處理的時候並無考慮到過濾參數,只須要在分頁標籤中添加過濾參數到傳輸數據的url
中便可!
標籤文件中的分頁處理修改以下:
1 ... 2 @register.simple_tag 3 def create_page_element(query_set, filter_conditions): 4 '''返回整個分頁元素''' 5 page_btns = '' 6 filters = '' 7 #過濾條件 8 for k, v in filter_conditions.items(): 9 filters += '&{0}={1}'.format(k, v) 10 added_dot_ele = False #標誌符 11 for page_num in query_set.paginator.page_range: 12 if page_num < 3 or page_num > query_set.paginator.num_pages -2 \ 13 or abs(query_set.number - page_num) <= 2: #表明最前2頁或最後2頁 #abs判斷先後1頁 14 element_class = "" 15 if query_set.number == page_num: 16 added_dot_ele = False 17 element_class = "active" 18 page_btns += '''<li class="%s"><a href="?page=%s%s">%s</a></li>''' % (element_class, page_num, filters ,page_num) 19 else: #顯示... 20 if added_dot_ele == False: #如今還沒加... 21 page_btns += '<li><a>...</a></li>' 22 added_dot_ele = True 23 return mark_safe(page_btns) 24 ...
上述文件中只是將過濾的參數值添加到提交url
的標籤中。
固然,在模板文件table_objs.html
中,咱們也要有相應的修改,這裏就簡單了只須要添加一個參數(filter_conditions
)便可:
1 ... 2 {% create_page_element query_set filter_conditions %} 3 ...
至此,BUG
解決了,能夠完美的進行過濾,分頁啦!
還有一點值得考慮,就是page
這個關鍵字,是否會影響到數據的存儲和查詢?爲了不這樣的隱患存在,咱們能夠將這個參數做爲保留字,不容許在數據庫中使用!
那麼,該如何實現呢?其實很簡答嘍,我只須要在過濾功能函數那裏添加一個判斷就行啦!以下:
1 #--------------------------過濾功能------------------------------ 2 def table_filter(request, admin_class): 3 """條件過濾,並構造濾後的數據結構""" 4 filter_conditions = {} 5 keywords = ['page'] #保留關鍵字 6 for k, v in request.GET.items(): 7 if k in keywords: 8 continue 9 if v: 10 filter_conditions[k] = v 11 return admin_class.model.objects.filter(**filter_conditions), filter_conditions
這裏咱們以列表的形式,便於後期擴展添加其餘的保留字!