在上篇文章中,咱们仅仅是展现了最基础的表格字段的内容,这篇文章咱们来添加过滤功能和分页功能!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
这里咱们以列表的形式,便于后期扩展添加其余的保留字!