項目一:CRM(客戶關係管理系統)--4過濾功能和分頁功能

在上篇文章中,咱們僅僅是展現了最基礎的表格字段的內容,這篇文章咱們來添加過濾功能和分頁功能!html

1. 添加分頁功能

 

  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

 

1.1. 添加分頁字段

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 #添加此數據覆蓋基類數據

 

1.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.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

2. 添加過濾功能

2.1. 添加過濾字段

一樣是在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

2.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

 

2.3. 編寫視圖函數

在基於分頁功能的基礎,添加過濾功能:

 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})

 

2.4. 編寫模板文件

由於過濾的條件須要提交後臺,因此要構建表單,在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)

渲染後的結果:

 

2.5. BUG解決

搜索後對結果非常滿意,分頁顯示數量也是正確,然而,當點擊下一頁的時候確調回了初始狀態!這是爲何呢??

其實很簡單,咱們在作分頁處理的時候並無考慮到過濾參數,只須要在分頁標籤中添加過濾參數到傳輸數據的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解決了,能夠完美的進行過濾,分頁啦!

2.6. 優化

還有一點值得考慮,就是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

這裏咱們以列表的形式,便於後期擴展添加其餘的保留字!

相關文章
相關標籤/搜索