day94.1
二。分页
""" page.py 自定义分页 使用方法: from utils.page import Pagination def users(request): current_page = int(request.GET.get('page',1)) total_item_count = models.UserInfo.objects.all().count() page_obj = Pagination(current_page,total_item_count,'/users.html') user_list = models.UserInfo.objects.all()[page_obj.start:page_obj.end] return render(request,'users.html',{'user_list':user_list,'page_html':page_obj.page_html()}) """ from django.utils.safestring import mark_safe class Pagination(object): def __init__(self,current_page,total_item_count,base_url=None,per_page_count=10,show_pager_count=11): """ :param current_page: 当前页 :param total_item_count: 数据库数据总条数 :param base_url: 分页前缀URL :param per_page_count: 每页显示数据条数 :param show_pager_count: 显示的页码数 """ try: current_page = int(current_page) except Exception as e: current_page = 1 self.current_page = current_page #当前页 self.total_item_count = total_item_count #数据库数据总条数 self.base_url = base_url #分页前缀URL self.per_page_count = per_page_count #每页显示数据条数 self.show_pager_count = show_pager_count #对多显示的页码 max_pager_num, b = divmod(total_item_count, per_page_count) #获取总页数及余数(有余数总页数加一) if b: max_pager_num += 1 self.max_pager_num = max_pager_num #初始化总页数 @property #定义开始数据函数,返回开始的条数 def start(self): """ :return: """ return (self.current_page-1)* self.per_page_count #当前页减一 * 每页显示的数据条数 @property #定义结束数据函数,返回结束的条数 def end(self): """ :return: """ return self.current_page * self.per_page_count #返回 当前页 * 每页显示的数据条数 #取到页码标签列表 def page_html(self): """ :return: """ page_list = [] # 处理上一页标签开始 if self.current_page == 1: #如果当前页为第一页 prev = ' <li><a href="#">上一页</a></li>' #上一页标签 链接为当前页 else: prev = ' <li><a href="%s?page=%s">上一页</a></li>' % (self.base_url,self.current_page - 1,) #上一页标签 链接为当前页减一 page_list.append(prev) #将li标签添加到页码列表中 # 处理上一页标签结束 half_show_pager_count = int(self.show_pager_count / 2) # 获取半数显示数量 # 定义数据特别少时的 开始页(第一页) 和 结束页(最大页加一); # 定义数据页数大于显示页数时: # 定义当前页未到显示页数一半时的 开始页(第一页) 和 结束页(显示页数加一); # 定义当前页达到显示页数一半 #如果 如果当前页 + 半数显示数量 > 最大显示数量 ,开始页(最大页数 - 显示页的数量) 结束页(最大页+1) #否则 开始页(当前页 - 半数显示页) 结束页 (当前页 + 半数显示页 + 1) #判断开始 if self.max_pager_num < self.show_pager_count: #如果最大页数 小于 显示页码数 # 页码小于11 pager_start = 1 #开始页为1 pager_end = self.max_pager_num + 1 #结束页为最大页加一 else: if self.current_page <= half_show_pager_count: #如果 当前页 小于等于 半数显示页数 pager_start = 1 # 开始页为1 pager_end = self.show_pager_count + 1 #结束页为显示页码数+1 else: if self.current_page + half_show_pager_count > self.max_pager_num: #如果当前页 + 半数显示数量 > 最大显示数量 pager_start = self.max_pager_num - self.show_pager_count + 1 #开始页码为 最大页码数 - 显示页码数 + 1(此处为显示最后11条) pager_end = self.max_pager_num + 1 # 结束页为显示页码数+1 else: # 当前页 + 半数显示数量 < 最大显示数量 pager_start = self.current_page - half_show_pager_count # 开始页为当前页 - 显示页的数量 pager_end = self.current_page + half_show_pager_count + 1 # # 判断结束 # 循环开始页至结束页,生成其他标签 及当前标签(活跃状态) for i in range(pager_start, pager_end): if i == self.current_page: #如果i为当前页 tpl = ' <li class="active"><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,) #标签为活跃状态 else: tpl = ' <li><a href="%s?page=%s">%s</a></li>' % (self.base_url,i, i,) #普通标签 page_list.append(tpl) #将标签加入 if self.current_page == self.max_pager_num: #如果当前页 等于 最大页 nex = ' <li><a href="#">下一页</a></li>' #下一页为# else: nex = ' <li><a href="%s?page=%s">下一页</a></li>' % (self.base_url,self.current_page + 1,) #下一页为当前页加一 page_list.append(nex) # 列表添加(下一页) return mark_safe(''.join(page_list)) # 拼接页码列表并返回 做网页显示安全处理
#cmdb分页的两种方式 page.py 下的 class Pagination(object): ..... def page_html_js(self): page_list = [] if self.current_page == 1: #如果当前页为第一页 prev = ' <li><a href="#">上一页</a></li>' else: #否则 上一页标签添加 onclick 函数 跳转当前页 - 1 prev = ' <li><a onclick="$.changePage(%s)">上一页</a></li>' %(self.current_page-1,) page_list.append(prev) #添加到页码列表 half_show_pager_count = int(self.show_pager_count / 2) # 数据特别少,15条数据=2页 if self.max_pager_num < self.show_pager_count: # 页码小于11 pager_start = 1 pager_end = self.max_pager_num + 1 else: if self.current_page <= half_show_pager_count: pager_start = 1 pager_end = self.show_pager_count + 1 else: if self.current_page + half_show_pager_count > self.max_pager_num: pager_start = self.max_pager_num - self.show_pager_count + 1 pager_end = self.max_pager_num + 1 else: pager_start = self.current_page - half_show_pager_count pager_end = self.current_page + half_show_pager_count + 1 for i in range(pager_start, pager_end): if i == self.current_page: tpl = ' <li class="active"><a onclick="$.changePage(%s)" >%s</a></li>' % (i,i,) #点击时执行自定制的jquary的扩展函数 else: tpl = ' <li><a onclick="$.changePage(%s)" >%s</a></li>' % (i, i,) page_list.append(tpl) if self.current_page == self.max_pager_num: nex = ' <li><a href="#">下一页</a></li>' else: nex = ' <li><a onclick="$.changePage(%s)" >下一页</a></li>' %(self.current_page+1,) page_list.append(nex) return ''.join(page_list) def page_html_test(self): page_list = [] if self.current_page == 1: prev = ' <li><a href="#">上一页</a></li>' else: prev = ' <li><a num="%s">上一页</a></li>' %(self.current_page-1,) page_list.append(prev) half_show_pager_count = int(self.show_pager_count / 2) # 数据特别少,15条数据=2页 if self.max_pager_num < self.show_pager_count: # 页码小于11 pager_start = 1 pager_end = self.max_pager_num + 1 else: if self.current_page <= half_show_pager_count: pager_start = 1 pager_end = self.show_pager_count + 1 else: if self.current_page + half_show_pager_count > self.max_pager_num: pager_start = self.max_pager_num - self.show_pager_count + 1 pager_end = self.max_pager_num + 1 else: pager_start = self.current_page - half_show_pager_count pager_end = self.current_page + half_show_pager_count + 1 for i in range(pager_start, pager_end): if i == self.current_page: tpl = ' <li class="active"><a num="%s" >%s</a></li>' % (i,i,) # 后期再加载html页面时,为他绑定事件,为所有的li绑定事件 拿到num,执行init函数 else: tpl = ' <li><a num="%s" >%s</a></li>' % (i, i,) page_list.append(tpl) if self.current_page == self.max_pager_num: nex = ' <li><a href="#">下一页</a></li>' else: nex = ' <li><a num="%s">下一页</a></li>' %(self.current_page+1,) page_list.append(nex) return ''.join(page_list)
三。组合搜索
server.hml 组合搜索生成下拉框 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css" /> <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css" /> <style> .loading{ position: fixed; top: 0; bottom: 0; right: 0; left: 0; background-color: white; opacity: 0.4; z-index: 1000; } .loading .img{ height: 32px; width: 32px; background: url('/static/img/loading.gif'); position: fixed; left: 50%; top:50%; margin-left: -16px; margin-top: -16px; z-index: 1001; } .search-area{ {# 定位搜索区域 #} position: relative; } .search-area .search-btn{ {# 定位搜索按钮 #} position: absolute; bottom: 1px; } .search-area .search-condition .condition{ {# 定义搜索条件区域 #} height: 35px; position: relative; } .search-area .search-condition .condition .icons{ {# 设置图标属性 #} position: absolute; left: 0; width: 35px; height: 35px; } .search-area .search-condition .condition .input-group{ {# 设置input框属性 #} position: absolute; left: 35px; right: 0; height: 35px; } </style> </head> <body> <div class="container"> <h1>服务器列表</h1> <!-- 组合搜索开始 --> <div class="search-area clearfix"> <div id="searchCondition" class="search-condition col-md-offset-2 col-md-8"> <div class="condition"> <div class="icons"> <a class="btn btn-default"> <i class="fa fa-plus-square"></i> </a> </div> <div class="input-group"> <div class="input-group-btn"> <label class="btn btn-default no-radius" type="button" style="width: 100px;"></label> <button type="button" class="btn btn-default dropdown-toggle no-border-r" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span></button> {#生成下拉栏按钮#} <ul class="change-search-condition dropdown-menu"> </ul> </div> </div> </div> </div> <div class="search-btn col-md-offset-10 col-md-2"> <a class="btn btn-primary">搜索</a> {# 搜索按钮#} </div> </div> <!-- 组合搜索结束 --> <!-- 数据开始 --> <table class="table table-bordered"> <thead id="tHead"> <tr> </tr> </thead> <tbody id="tBody"> </tbody> </table> <!-- 数据结束 --> <!-- 分页开始 --> <ul id="pagination" class="pagination"></ul> <!-- 分页结束 --> </div> <div id="loading" class="loading hide"> <div class="img"></div> </div> <script src="/static/js/jquery-3.2.1.js"></script> <script src="/static/plugins/bootstrap/js/bootstrap.js"></script> {#导入bootstrap#} <script src="/static/js/nb-list.js"></script> <script> $(function () { $.nBList("/server_json.html"); }) </script> </body> </html>
1 #views.py 2 3 import json 4 from django.shortcuts import render,HttpResponse 5 from django.http import JsonResponse 6 from repository import models 7 from utils.page import Pagination 8 9 def server(request): 10 return render(request,'server.html') 11 12 def server_json(request): 13 14 search_config = [ #定义搜索配置 15 {'name': 'server_status_id', 'title': '服务器状态', 'type': 'select', 'choice_name': 'status_choices'}, 16 {'name': 'hostname','title':'主机名','type':'input'}, 17 {'name': 'cabinet_num', 'title': "机柜号", 'type': 'input'}, 18 19 ] 20 21 table_config = [ 22 { 23 'q': None, 24 "display": True, 25 'title': '选择', 26 'text': {'tpl':'<input type="checkbox" value="{nid}" />','kwargs':{'nid': '@id' }}, 27 'attr':{'class':'c1','nid':'@id'}, 28 29 }, 30 { 31 'q': 'id', 32 "display": False, 33 'title': 'ID', 34 'text': {'tpl': '{a1}', 'kwargs': {'a1': '@id'}}, 35 'attr': {}, 36 }, 37 { 38 'q': 'hostname', 39 "display": True, 40 'title': '主机名', 41 'text': {'tpl': '{a1}-{a2}','kwargs':{'a1': '@hostname','a2':'666'}}, 42 'attr': {'class': 'c1'}, 43 }, 44 { 45 'q': 'sn', 46 'title': '序列号', 47 "display": True, 48 'text': {'tpl': '{a1}','kwargs':{'a1': '@sn'}}, 49 'attr': {'class': 'c1'}, 50 }, 51 { 52 'q': 'os_platform', 53 'title': '系统', 54 "display": True, 55 'text': {'tpl': '{a1}','kwargs':{'a1': '@os_platform'}}, 56 'attr': {'class': 'c1'}, 57 }, 58 { 59 'q': 'os_version', 60 "display": True, 61 'title': '系统版本', 62 'text': {'tpl': '{a1}','kwargs':{'a1': '@os_version'}}, 63 'attr': {'class': 'c1'}, 64 }, 65 { 66 'q': 'business_unit__name', 67 "display": True, 68 'title': '业务线', 69 'text': {'tpl': '{a1}','kwargs':{'a1': '@business_unit__name'}}, 70 'attr': {'class': 'c1'}, 71 }, 72 { 73 'q': 'server_status_id', 74 "display": True, 75 'title': '服务器状态', 76 'text': {'tpl': '{a1}', 'kwargs': {'a1': '@@status_choices'}}, 77 'attr': {'class': 'c1'}, 78 }, 79 { 80 'q': None, 81 "display": True, 82 'title': '操作', 83 'text': {'tpl': '<a href="/edit/{nid}/">编辑</a> | <a href="/del/{uid}/">删除</a> ', 'kwargs': {'nid': '@id','uid': '@id'}}, 84 'attr': {}, 85 }, 86 ] 87 88 values = [] 89 for item in table_config: 90 if item['q']: 91 values.append(item['q']) 92 93 # 获取用户请求的页码 94 current_page = request.GET.get('pageNum') 95 total_item_count = models.Server.objects.all().count() 96 #每页显示两条 97 page_obj = Pagination(current_page, total_item_count,per_page_count=2) 98 99 server_list = models.Server.objects.values(*values)[page_obj.start:page_obj.end] 100 101 """ 102 [ 103 {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, 104 {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, 105 {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, 106 {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, 107 {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, 108 ] 109 110 """ 111 response = { 112 'search_config':search_config, #发送搜索配置 113 'data_list': list(server_list), 114 'table_config': table_config, 115 'global_choices_dict':{ 116 'status_choices': models.Server.server_status_choices 117 }, 118 'page_html': page_obj.page_html_js() 119 } 120 121 return JsonResponse(response) 122 123 124 125 def disk(request): 126 return render(request,'disk.html') 127 128 def disk_json(request): 129 table_config = [ 130 { 131 'q': None, 132 'title': '选择', 133 'text': {'tpl': '<input type="checkbox" value="{nid}" />', 'kwargs': {'nid': '@id'}}, 134 }, 135 { 136 'q': 'id', 137 'title': 'ID', 138 'text': {'tpl': '{nid}', 'kwargs': {'nid': '@id'}}, 139 }, 140 { 141 'q': 'slot', 142 'title': '槽位', 143 'text': {'tpl': '{nid}', 'kwargs': {'nid': '@slot'}}, 144 }, 145 ] 146 147 values = [] 148 for item in table_config: 149 if item['q']: 150 values.append(item['q']) 151 server_list = models.Disk.objects.values(*values) 152 response = { 153 'data_list': list(server_list), 154 'table_config': table_config 155 } 156 157 return JsonResponse(response) 158 159 160 161 def xxxxx(server_list): 162 # [{},{}] 163 for row in server_list: 164 for item in models.Server.server_status_choices: 165 if item[0] == row['server_status_id']: 166 row['server_status_id_name'] = item[1] 167 break 168 yield row 169 170 171 172 def test(request): 173 """ 174 赠送,模板语言显示choice 175 :param request: 176 :return: 177 """ 178 # server_list = models.Server.objects.all() 179 # for row in server_list: 180 # print(row.id,row.hostname,row.business_unit.name,"===",row.server_status_id,row.get_server_status_id_display() ) 181 182 # server_list = models.Server.objects.all().values('hostname','server_status_id') 183 # for row in server_list: 184 # for item in models.Server.server_status_choices: 185 # if item[0] == row['server_status_id']: 186 # row['server_status_id_name'] = item[1] 187 # break 188 189 data_list = models.Server.objects.all().values('hostname', 'server_status_id') 190 191 return render(request,'test.html',{'server_list':xxxxx(data_list)})
/** * nb-list.js */ (function (jq) { var requestUrl = ""; var GLOBAL_CHOICES_DICT = { // 'status_choices': [[0,'xxx'],] // 'xxxx_choices': [[0,'xxx'],] }; function csrfSafeMethod(method) { //!定义csrf函数 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); //? } $.ajaxSetup({ beforeSend: function (xhr, settings) { //请求头中设置一次csrf-token if (!csrfSafeMethod(settings.type)) { xhr.setRequestHeader('X-CSRFToken', $.cookie('csrftoken')); } } }); function getChoiceNameById(choice_name, id) { var val; var status_choices_list = GLOBAL_CHOICES_DICT[choice_name]; $.each(status_choices_list, function (kkkk, vvvv) { if (id == vvvv[0]) { val = vvvv[1]; } }); return val; } String.prototype.format = function (args) { return this.replace(/\{(\w+)\}/g, function (s, i) { return args[i]; }); }; /* 像后台获取数据 */ function init(pageNum) { $('#loading').removeClass('hide'); var condition = getSearchCondition(); //初始化获取搜索条件 $.ajax({ url: requestUrl, type: 'GET', data: {'pageNum': pageNum, 'condition': JSON.stringify(condition)}, //? dataType: 'JSON', success: function (response) { /* 处理choice */ GLOBAL_CHOICES_DICT = response.global_choices_dict; /* 处理搜索条件 */ initSearchCondition(response.search_config); /* 处理表头 */ initTableHead(response.table_config); /* 处理表内容 */ initTableBody(response.data_list, response.table_config); /* 处理表分页 */ initPageHtml(response.page_html); $('#loading').addClass('hide'); }, error: function () { $('#loading').addClass('hide'); } }) } /* 绑定搜索条件事件 */ function bindSearchConditionEvent() { // 改变下拉框内容时 $('#searchCondition').on('click', 'li', function () { //? // $(this) = li标签 // 找到文本修改 $(this).parent().prev().prev().text($(this).text()); // 找input标签,修改,重建 $(this).parent().parent().next().remove(); var name = $(this).find('a').attr('name'); var type = $(this).find('a').attr('type'); if (type == 'select') { var choice_name = $(this).find('a').attr('choice_name'); // 作业:生成下拉框, var tag = document.createElement('select'); tag.className = "form-control no-radius"; tag.setAttribute('name', name); $.each(GLOBAL_CHOICES_DICT[choice_name], function (i, item) { var op = document.createElement('option'); op.innerHTML = item[1]; op.setAttribute('value', item[0]); $(tag).append(op); }) } else { // <input class="form-control no-radius" placeholder="逗号分割多条件" name="hostnmae"> var tag = document.createElement('input'); tag.setAttribute('type', 'text'); //设置'type属性'为‘text' // $(tag).addClass('form-control no-radius') tag.className = "form-control no-radius"; tag.setAttribute('placeholder', '请输入条件'); tag.setAttribute('name', name); } $(this).parent().parent().after(tag); }); // 添加搜索条件 $('#searchCondition .add-condition').click(function () { //当单价添加条件按钮时触发以下函数 var $condition = $(this).parent().parent().clone(); // 条件对象 = 当前按钮父亲的父亲的克隆 $condition.find('.add-condition').removeClass('add-condition').addClass('del-condition').find('i').attr('class', 'fa fa-minus-square'); //? $condition.appendTo($('#searchCondition')); //? }); // 删除搜索条件 $('#searchCondition').on('click', '.del-condition', function () { $(this).parent().parent().remove(); }); //点击搜索按钮 $('#doSearch').click(function () { init(1); }) } function initSearchCondition(searchConfig) { if (!$('#searchCondition').attr('init')) { // 找到页面上的搜索条件标签 // 根据searchConfig生成li var $ul = $('#searchCondition :first').find('ul'); $ul.empty(); initDefaultSearchCondition(searchConfig[0]); $.each(searchConfig, function (i, item) { var li = document.createElement('li'); var a = document.createElement('a'); a.innerHTML = item.title; a.setAttribute('name', item.name); a.setAttribute('type', item.type); if (item.type == 'select') { a.setAttribute('choice_name', item.choice_name); } $(li).append(a); $ul.append(li); }); $('#searchCondition').attr('init', 'true'); } } function initDefaultSearchCondition(item) { // item={'name': 'hostname','title':'主机名','type':'input'}, if (item.type == 'input') { var tag = document.createElement('input'); tag.setAttribute('type','text'); //? 设置标签属性'type'='text' // $(tag).addClass('form-control no-radius') tag.className = "form-control no-radius"; tag.setAttribute('placeholder', '请输入条件'); tag.setAttribute('name', item.name); } else { var tag = document.createElement('select'); tag.className = "form-control no-radius"; tag.setAttribute('name',item.name); //? 设置标签属性name为 $.each(GLOBAL_CHOICES_DICT[item.choice_name], function (i, row) { var op = document.createElement('option'); op.innerHTML = row[1]; op.setAttribute('value', row[0]); $(tag).append(op); }) } $('#searchCondition').find('.input-group').append(tag); $('#searchCondition').find('.input-group label').text(item.title); } function getSearchCondition() { var result = {}; $('#searchCondition').find('input[type="text"],select').each(function(){ var name = $(this).attr('name'); var val = $(this).val(); if(result[name]){ result[name].push(val); }else{ result[name] = [val ]; } }); return result; } function initPageHtml(page_html) { $('#pagination').empty().append(page_html); } function initTableHead(table_config) { /* table_config = [ { 'q': 'hostname', 'title': '主机名', }, { 'q': 'sn', 'title': '序列号', }, { 'q': 'os_platform', 'title': '系统', }, ] */ $('#tHead tr').empty(); $.each(table_config, function (k, conf) { if (conf.display) { var th = document.createElement('th'); th.innerHTML = conf.title; $('#tHead tr').append(th); } }); } function initTableBody(data_list, table_config) { /* [ {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, ] <tr> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> </tr> */ $('#tBody').empty(); $.each(data_list, function (k, row_dict) { // 循环数据库中获取到的每行数据 // row_dict = {'hostname':'xx', 'sn':'xx', 'os_platform':'xxx'}, var tr = document.createElement('tr'); $.each(table_config, function (kk, vv) { if (vv.display) { var td = document.createElement('td'); /* 处理Td内容 */ var format_dict = {}; $.each(vv.text.kwargs, function (kkk, vvv) { if (vvv.substring(0, 2) == "@@") { var name = vvv.substring(2, vvv.length); // status_choices var val = getChoiceNameById(name, row_dict[vv.q]); format_dict[kkk] = val; } else if (vvv[0] == "@") { var name = vvv.substring(1, vvv.length); format_dict[kkk] = row_dict[name]; } else { format_dict[kkk] = vvv; } }); td.innerHTML = vv.text.tpl.format(format_dict); /* 处理Td属性 */ $.each(vv.attr, function (attrName, attrVal) { if (attrVal[0] == "@") { attrVal = row_dict[attrVal.substring(1, attrVal.length)] } td.setAttribute(attrName, attrVal); }); $(tr).append(td); } }); $('#tBody').append(tr); }) } jq.extend({ "nBList": function (url) { requestUrl = url; init(1); bindSearchConditionEvent(); //执行绑定搜索条件函数 }, "changePage": function (pageNum) { init(pageNum); } }); })(jQuery);