bootstrap-table+Django: 服务端分页(包括搜索查询功能)

1.分页方式:

bootstrap-table提供两种分页方式,client和server,即客户端和服务端分页;

特点:

  • client端分页:后台返回所有数据,前台翻页时不再请求后台。
  • server端分页:后台根据前台每次翻页时传递的参数,进行切片查询数据,每次只返回对应页面的数据。

弊端:

client端分页:

  1. 后台一次查询所有数据,对服务器造成压力交大;
  2. 当页面存在bootstrap-switch时,由于我是在bootstrap-table中的onLoadSuccess加载完表格后渲染switch开关,
  3. 当使用client分页时,只进行了一次后台查询,也就是执行了一次onLoadSuccess函数,所以除了第一页开关按钮正常,其他页面都没有渲染出开关。

server端分页:
    暂无

2.实现代码:

2.1 前端

2.1.1html

引入相应的包之后即可使用

<section class="section  bg-light">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 text-center">
<div class="section-title mb-4 pb-2">
<h4 class="title mb-4">Data Structure List</h4>
</div>
</div><!--end col-->
</div><!--end row-->

<div id="toolbar">
<div class="form-inline" role="form">
{# <select class="form-control custom-select"#}
{# style="margin-bottom: auto;">#}
{# <option value="">导出基本信息</option>#}
{# <option value="all">导出所有信息</option>#}
{# <option value="selected">导出选定列信息</option>#}
{# </select>#}
{# <input class="btn btn-soft-primary btn-sm" type="button" id="btn1" value="显示所有列"#}
{# onclick="hideCol()">#}
<div id="add_data_structure" style="left: 100px">
<button id="remove" class="btn btn-soft-primary btn-sm"
onclick="window.open('/teams/add_data_structure/')">
<i class="fa fa-plus"></i> Add new data structure
</button>
</div>
<!-- 自定义搜索查询 -->
<div class="float-right search btn-group" style="left: 10px">
<div class="input-group"><input id="search-keyword" class="form-control search-input" type="search" placeholder="Search" autocomplete="off">
<div class="input-group-append">
<button id="search-button" class="btn btn-soft-primary btn-sm" type="button" name="search" title="Search">
<i class="fa fa-search"></i></button>
</div>
</div>
</div>
</div>
</div>
<!-- data structure index pagination -->
<table id="table"
class="table-sm small"
data-pagination="true"
data-page-size="20"
data-buttons-class="soft-primary btn-sm"
data-sort-name="created_date"
data-sort-order="desc"
data-remember-order="true"

data-show-fullscreen="true"
data-show-columns="true"
data-show-columns-toggle-all="true"
data-show-export="true"

data-click-to-select="true"
data-toolbar="#toolbar"
{# style="height: 100vh;overflow: auto"#}
>
<thead class="thead-light">
</table>
</div>
</section>

2.1.2 js

js同理,需要引入相应的包即可使用

{% block script %}
<!-- JS for download table-->
<script>
var $table = $('#table')
$(function () {
$('#toolbar').find('select').change(function () {
$table.bootstrapTable('destroy').bootstrapTable({
exportDataType: $(this).val(),
exportTypes: ['excel', 'xml', 'csv', 'txt', 'sql'],
columns: [
{
field: 'state',
checkbox: true,
visible: $(this).val() === 'selected'
}
]
})
}).trigger('change')
})
</script>
<!-- JS for pagination -->
<script>
var $articlesTable = $('#table').bootstrapTable('destroy').bootstrapTable({
url: '/teams/data_structure_index/',
method: 'GET',
dataType: "json",
uniqueId: 'id', //每一行的唯一标识,一般为主键列
striped: false, //是否显示行间隔色
cache: false,
sortName: 'no',
sortable: true,
sortOrder: 'desc',
sidePagination: "server",
undefinedText: '--',
singleSelect: true,
toolbar: '#toolbar', //工具按钮用哪个容器
showToggle: true, //是否显示详细视图和列表视图的切换按钮
{#search: true, //是否显示表格搜索#}
{#searchOnEnterKey: true, //回车后执行搜索#}
{#showSearchButton: true, //搜索确定按钮#}
strictSearch: true,
clickToSelect: true,
pagination: true, //是否显示分页(*)
showRefresh: true, //是否显示刷新按钮
pageNumber: 1, //初始化加载第一页,默认第一页
pageSize: 10, //每页的记录行数(*)
pageList: [10, 20, 50, 100, 'all'],
paginationPreText: "<",
paginationNextText: ">",
queryParamsType: "",
maxHeight: "200px",
queryParams: function (params) {
var query_params = {
'pageSize': params.pageSize,
'pageNumber': params.pageNumber,
'search_kw': $('#search-keyword').val(), // 查询框中的参数传递给后台
'sortName': params.sortName,
'sortOrder': params.sortOrder
};
return query_params;
},
columns: [
{
field: 'no',
title: 'No',
valign: 'middle',
align: 'center',
halign: 'center',
width: '70px',
visible: true,
sortable: true,
formatter: function (value, row, index) {
{#console.log(row)#}
var result = "";
result += '<a href="/teams/data_structure_detail/' + row.id + '/">' + row.no + '</a>'
return result
}
},
{
field: 'id',
title: 'No',
valign: 'middle',
align: 'center',
halign: 'center',
width: '70px',
visible: false,
sortable: true,
formatter: function (value, row, index) {
{#console.log(row)#}
var result = "";
result += '<a href="/teams/data_structure_detail/' + row.id + '/">' + row.id + '</a>'
return result
}
},
{
field: 'title',
title: 'title',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
formatter: function (value, row, index) {
var result = "";
result += '<a href="/teams/data_structure_detail/' + row.id + '/">' + row.title + '</a>'
return result
}
},
{
field: 'description',
title: 'Description',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
},
{
field: 'team',
title: 'Team',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
},
{
field: 'knowledge_category',
title: 'Knowledge Category',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
},
{
field: 'full_name',
title: 'Owner',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
sortable: true,
},
{
field: 'created_date',
title: 'Upload date',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
sortable: true,
},
{
field: 'attachments',
title: 'Files',
valign: 'middle',
align: 'left',
halign: 'center',
width: 'auto',
visible: true,
formatter: function (value, row, index) {
var result = "";
for (var i = 0; i < row.attachments.id.length; i++) {
result += '<a href="/file/teams_data_structure_file_download/' + row.attachments.id[i] + '">' + row.attachments.file_name[i] + ';<br/></a>'
}
return result
}
},
{
title: 'operation',
valign: 'middle',
halign: 'center',
width: '10%',
visible: true,
formatter: function (value, row, index) {
var result = "";
result += '<a href="/teams/data_structure_detail/' + row.id + '/" target="_blank"><i class="fas fa-edit"></i></a>  '
result += '<a href="#" onclick="del_data_structure(' + row.id + ')"><i class="fas fa-trash-alt"></i></a>'
return result
}
}
],
onLoadError: function () {
console.log("数据加载失败!", "错误提示");
},
});
// 搜索查询按钮触发事件
$("#search-button").click(function () {
console.log($('#search-keyword').val())
$('#table').bootstrapTable(('refresh')); // 很重要的一步,刷新url!
})
// 回车执行搜索
$("#search-keyword").bind('keyup', function (event) {
console.log($('#search-keyword').val())
$('#table').bootstrapTable(('refresh')); // 很重要的一步,刷新url!
})
</script>
<!-- JS for delete data -->
<script>
function del_data_structure(id) {
console.log(id)
$.messager.confirm({
title: '提示', msg: '是否确认删除该条数据?', top: 200, fn: function (r) {
if (r) {
$.ajax({
url: server_url + '/teams/data_structure_detail/' + id + '/',
method: 'delete',
processData: false,
contentType: false,
cache: false,
success: function (data) {
console.log("data:" + data);
console.log("data:" + data.status);
if (data.status === 200) {
$.messager.alert({title: '提示', msg: data.msg, icon: 'warning', top: 200,});
$.messager.show({
title: '提示',
msg: data.msg,
showType: '',
timeout: 500,
style: {top: 200}
});
console.log("data:" + data.msg);
$('#table').bootstrapTable('refresh');
{#window.setTimeout("window.location=server_url+'/teams/data_structure/'", 600);#}
return
}
console.log(data)
$.messager.alert({title: '提示', msg: '权限不足或服务请求异常,数据无法删除!', icon: 'warning', top: 200});
},
//请求失败,包含具体的错误信息
error: function (data) {
console.log('error' + data.msg);
$.messager.alert({title: '提示', msg: '请求服务错误或当前网络不佳!', icon: 'warning', top: 200});
}
});
}
}
});
}
</script>
{% endblock %}

2.1.3 Django后端

APIView使用了DRF框架,django内置的是View
查询功能可根据自己的需求更改

class DataStructureListView(APIView):
def get(self, request):
pageSize = int(request.GET.get('pageSize', 10))
pageNumber = int(request.GET.get('pageNumber', 1))
search_kw = request.GET.get('search_kw', '')
sortName = request.GET.get('sortName', '')
sortOrder = request.GET.get('sortOrder', '')

ds_all = DataStructure.objects.all()
# 查询
if search_kw:
ds_all = ds_all.filter(
Q(title__icontains=search_kw) | Q(description__icontains=search_kw) | Q(full_name__icontains=search_kw))
if sortName == 'no':
sortName = 'id'
# 排序
if sortOrder == 'desc':
ds_list = ds_all.order_by('-{}'.format(sortName))[(pageNumber - 1) * pageSize:(pageNumber) * pageSize]
else:
ds_list = ds_all.order_by('{}'.format(sortName))[(pageNumber - 1) * pageSize:(pageNumber) * pageSize]
# print(ds_list)
ds_list_len = len(ds_list)
rows = []
total = ds_all.count()
data = {"total": total, "rows": rows}
for i in range(ds_list_len):
attachments = {}
attachments['file_name'] = [i.filename for i in ds_list[i].attachments.all()]
attachments['id'] = [i.id for i in ds_list[i].attachments.all()]
if sortOrder == 'desc':
no = total - (pageNumber - 1) * pageSize - i
else:
no = (pageNumber - 1) * pageSize + i
row = {
'id': ds_list[i].id,
'no': no,
'title': ds_list[i].title,
'description': ds_list[i].description,
'team': ds_list[i].team,
'created_date': ds_list[i].created_date,
'full_name': ds_list[i].full_name,
'knowledge_category': ds_list[i].knowledge_category,
'attachments': attachments,
}
rows.append(row)
return JsonResponse(data)