问题背景:
在笔者的练习项目中, 用户在登录之后, 需要查询当前系统已经录入了多少本书。 那么这就带来了以下问题:
- 需要保存用户输入的查询条件
- 后端需要处理用户输入的查询条件, 并返回相应的数据
- 前端能以表格的形式展示数据
- 前端能通过指定的数据项进行升序/降序排列
- 前端能指定表格一页所展示多少行数据
这个应用场景, 几乎在所有的后端应用中都存在。 那么在本文中将要重点讲述怎么处理前端怎么通过表格展示后端的数据
Metronic 模版中的表格
Metronic中有很多的表格模板, 大家可以根据自己的需要选择其中一种就可以。 先看一下Metronic的表格的HTML实现。
<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
<thead>
<tr>
<th>
<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
<input type="checkbox" class="group-checkable" data-set="#sample_1 .checkboxes" />
<span></span>
</label>
</th>
<th> Username </th>
<th> Email </th>
<th> Status </th>
<th> Joined </th>
<th> Actions </th>
</tr>
</thead>
<tbody>
<tr class="odd gradeX">
<td>
<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
<input type="checkbox" class="checkboxes" value="1" />
<span></span>
</label>
</td>
<td> shuxer </td>
<td>
<a href="mailto:shuxer@gmail.com"> shuxer@gmail.com </a>
</td>
<td>
<span class="label label-sm label-success"> Approved </span>
</td>
<td class="center"> 12 Jan 2012 </td>
<td>
<div class="btn-group">
<button class="btn btn-xs green dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false"> Actions
<i class="fa fa-angle-down"></i>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="javascript:;">
<i class="icon-docs"></i> New Post </a>
</li>
<li>
<a href="javascript:;">
<i class="icon-tag"></i> New Comment </a>
</li>
<li>
<a href="javascript:;">
<i class="icon-user"></i> New User </a>
</li>
<li class="divider"> </li>
<li>
<a href="javascript:;">
<i class="icon-flag"></i> Comments
<span class="badge badge-success">4</span>
</a>
</li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
根据以上代码, 比较好理解, <thead></thead>标签中的是表格标题栏。 图例中的标题栏,第一列是一个checkbox控件。 然后依次是Username/Email/Status/Joined/Actions。
其效果如下图所示:
在<tbody></tbody>标签中的是表格内容栏。在图例中, 一共有25个记录。是在模版文件中一共有25个<tr></tr>,该标签定义HTML中的一行, 一个<tr></tr>表示一行。
在本文所给出的HTML实现中,为了缩减篇幅,特意只保留了一行。
在Metronic的模板示例文件中包含了以下内容
<script src="{% static '/global/scripts/datatable.js' %}" type="text/javascript"></script>
<script src="{% static '/global/plugins/datatables/datatables.min.js' %}" type="text/javascript"></script>
<script src="{% static '/global/plugins/datatables/plugins/bootstrap/datatables.bootstrap.js' %}"
type="text/javascript"></script>
这说明Metronic使用的是datatable插件。 搜索了一下,datatable的官方网站在这里传送门。 官方介绍如下:
DataTables is a plug-in for the jQuery Javascript library. It is a highly flexible tool, based upon the foundations of progressive enhancement, and will add advanced interaction controls to any HTML table.
我们先不要管datatable的诸多功能特性。 目前面临的问题是, 我们怎么根据网站的实际内容, 生成前端表格所需要的内容,即<tr></tr>标签的内容。
使用Django模板生成表格内容
在笔者的练习项目中,需要展示书名/出版社/借阅次数/数量/作者/状态/ISBN这些信息。 那么对表格内容的处理如下, 使用django的模板语言
{% for item in items %}和{%s endfor %}, 就可以展示来自服务器的数据呢
<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
<thead>
<tr>
<th>
<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
<input type="checkbox" class="group-checkable" data-set="#id_booksTable .checkboxes"/>
<span></span>
</label>
</th>
<th> 书名</th>
<th> 出版社</th>
<th> 借阅次数</th>
<th> 数量</th>
<th> 作者</th>
<th> 状态</th>
<th> ISBN</th>
</tr>
</thead>
<tbody>
</tbody>
<tbody>
{% for item in items %}
<tr class="odd gradeX">
<td>
<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
<input type="checkbox" class="checkboxes" value="1"/>
<span></span>
</label>
</td>
<td>{{ item.name }}</td>
<td>{{ item.press }}</td>
<td>{{ item.lendCount }}</td>
<td>{{ item.acount }}</td>
<td>{{ item.author }}</td>
<td>{{ item.status }}</td>
<td>{{ item.isbn }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Django后台处理
def get(self, request):
items = []
testline = {}
for i in range(100):
testline['bookname'] = "Python Web开发测试驱动方法"
testline['press'] = "人民邮电出版社"
testline['lendCount'] = 4
testline['amount'] = 5
testline['author'] = "Harry J.W. Percival"
testline["importTime"] = "2017/12/29"
testline["status"] = "busy"
testline["class"] = "IT"
testline["isbn"] = 1234567
items.append(testline.copy())
return render(request, 'p_books.html', {'items': items})
这里,我使用for循环生成来100个测试数据。让我们来看一下效果:
数据是已经出来了, 但存在以下几个问题:
- 第一行“No data available in table”
- 100行数据一起显示出来,并没有根据一页显示20个记录的设置来显示
这说明我提出的办法并不是datatable插件所接受的。
那究竟该怎样做呢?
HTML实现
<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
<thead>
<tr>
<th>
<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
<input type="checkbox" class="group-checkable" data-set="#id_booksTable .checkboxes"/>
<span></span>
</label>
</th>
<th> 书名</th>
<th> 出版社</th>
<th> 借阅次数</th>
<th> 数量</th>
<th> 作者</th>
<th> 状态</th>
<th> ISBN</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
注意, 这里我们并不需要写<tbody></tbody>这里面的内容,就让datatable插件帮我们处理吧
js实现
$(document).ready(function() {
$("#sample_1").dataTable(
{
searching : false,
destroy : true,
"ajax": {
"url": "book/query/",
"type": "get",
"dataSrc":"",
},
columns: [
{ },
{ data: 'name' },
{ data: 'press' },
{ data: 'lendCount' },
{ data: 'acount' },
{ data: 'author' },
{ data: 'status' },
{ data: 'isbn' }
],
'columnDefs': [{
'targets': 0,
'searchable': false,
'orderable': false,
'className': 'select-checkbox',
'render': function (data, type, full, meta){
return '<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline"><input type="checkbox" class="checkboxes" value="1"/><span></span></label>'
}
}],
}
);
});
在这里面需要注意的是,ajax里面需要说明选项dataSrc为"",这是因为我们在Django实现中以json array直接返回的数据。
Django 实现
def get(self,request):
if request.method == "GET":
objects = Book.objects.all()
dic = [obj.as_dict() for obj in objects]
return HttpResponse(json.dumps(dic), content_type="application/json")
as_dict的定义在book model中:
class Book(models.Model):
name = models.CharField(max_length=48)
author = models.CharField(max_length=24)
isbn = models.IntegerField()
status = models.CharField(max_length=12)
press = models.CharField(max_length=48)
acount = models.IntegerField()
lendCount = models.IntegerField()
def as_dict(self):
return {
"name": self.name,
"author": self.author,
"isbn" : self.isbn,
"status" : self.status,
"press" : self.press,
"acount" : self.acount,
"lendCount" : self.lendCount
}
这里需要注意的是需要在对应的url book/query/中定义get方法,并以json格式返回数据。
在如此这般操作之后, 效果如下:
可以看到数据已经从后台传到了前台。 并且datatable插件已经帮我们处理好了,分页,排序功能。
再思考
因为采取的是 client processing的处理方式, 这种方式需要服务器一次性把需要显示的数据全部发给前端。 那么这就有了以下几个问题:
- 数据库太大的时候,比如有百万级别数据的时候, 一次性发送json数据的速度就有待考证。
- 前端一次性获得数据之后,再分页的时候,都是上次浏览的时候缓存的数据处理的,那么在用户眼前的数据可能并不是服务器中实时的数据
怎么解决这两个问题, 就需要使用server processing的处理方式。 下次再讲
参考文献:
- 官方文档,关于data source的说明传送门
- 参考连接对ajax request 和dataSrc,
以及返回数据的说明 https://www.codeproject.com/Articles/1081121/jQuery-Datatable-With-Server-Side-Data