Python 全栈系列64 - 使用Flask+DataTables+Mongo搭建交互式表格
作者:互联网
说明
差不多凑齐7颗龙珠,可以在前端做一个交互式的表格页面了。主要用于快速的检索和录入数据。
- 1 datatables: 提供了自动的分页、检索,以及渲染单元格样式
- 2 icheck: 提供了行的checkbox
- 3 awesomefont: 提供小图标,看起来方便
- 4 jquery: 控制前后端的数据交互以及前端页面的修改
- 5 flask: 提供后端的数据处理
- 6 mongo: 提供非结构化的字典存储,方便修改
- 7 modal: 提供更友好的编辑提示
使用场景:提供快速的录入构建图结构的入口。
A 内容
整体上处理的步骤大约是这样
- 1 H5页面:访问页面
- 2 Flask读取:页面载入是请求数据,此时从Mongo里读取数据
- 3 Flask处理:将读取的非结构化数据转化为表格,以及准备datatables格式化需要的表头
- 4 Ajax,DataTables:获取到数据后将数据灌入,使用DataTables格式化
- 5 JQuery格式化:使用Jquery对某些列的格式进行处理,展示起来更方便
- 6 Datatables:用户使用表格快速搜索,浏览
- 7 模态框:用户通过模态框进行表格内容的修改
- 8 Ajax异步提交:使用Ajax提交更改(选定行)
- 9 Flask处理:Flask获取更改的数据,并处理为合适的格式
- 10 Flask写入进行写入后返回更改的数据,将这部分数据送回(而不是整个表格)进行异步刷新
约定:每行数据都有一列可以起到主键的作用(唯一),在插入数据时只采用主键匹配的方式。
1 H5页面:访问页面
基于jinja搭建的基础模板(base.html
)可以参考这篇文章,该模板提供了基础的样式和js组件导入,不再细说。
现在的页面view10.html
,整个的h5页面的布局以行为基本单位,其他的暂时不用去管。
{% extends './main/index.html'%}
{%block pagecontent%}
<!-- 空白行作为间隔 -->
<div class="m-5"></div>
<!-- row1 -->
<div class="row">
<div class="col">
<button id="btn" class="btn btn-primary">test</button><br><br>
<!-- 步骤一:表头 -->
<table id="table_id_example" class="display">
<thead>
<tr>
<th width="20px"><input type="checkbox" class="icheckbox_minimal" id="all_checked"></th>
<th>姓名</th>
<th>年龄</th>
<th>称呼</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
</table>
</div>
</div>
{%endblock%}
2 Flask读取:页面载入是请求数据,此时从Mongo里读取数据
先实现功能,但是之后也应该将Mongo的数据库操作封装为一个类。
# 返回所有测试记录
def get_test_db_recs(**mongo_cfg):
with MongoClient(mongo_cfg['host'], mongo_cfg['port']) as conn:
db = conn.admin
db.authenticate(mongo_cfg['user'], mongo_cfg['pwd'])
db = conn[mongo_cfg['source_db_name']]
collection = db[mongo_cfg['source_collect_name']]
some_one = collection.find({}, {'_id': 0, 'name': 1, 'age': 1, 'title': 1,'status':1})
# 连接关闭前把游标里的数拿出来
some_one = list(some_one)
return some_one
---
[{'name': 'andy', 'age': 11, 'title': 'mr', 'status': 1}, {'name': 'billy', 'age': 12, 'title': 'mr', 'status': 1}, {'name': 'cindy', 'age': 12, 'title': 'ms', 'status': 1}]
这个函数获取了mongo库里的所有数据(不带筛选),并且挑选了name, age, title, status
四列数据。由于mongo的_id
是一个对象,因此就不拿了,这需要在数据中自己保留一个主键字段。
# 获取数据的视图函数,返回json
@app1.route('/view13/', methods=['GET'])
def view13():
res = get_test_db_recs(**mongo_cfg)
print(res)
res_df = pd.DataFrame(res, columns=['name', 'age', 'title','status'])
res_json = res_df.to_json(orient='records')
res_dict = {}
res_dict['data'] = json.loads(res_json)
return jsonify(res_dict)
获取的mongo数据是一个列表,每个元素对应一行的数据。将其转换为DataFrame,再利用DataFrame自带的转json功能将数据转为json格式。
{
"data": [
{
"age": 11,
"name": "andy",
"status": 1,
"title": "mr"
},
{
"age": 12,
"name": "billy",
"status": 1,
"title": "mr"
},
{
"age": 12,
"name": "cindy",
"status": 1,
"title": "ms"
}
]
}
3 Flask处理:将读取的非结构化数据转化为表格,以及准备datatables格式化需要的表头
这一步可以是直接从数据库获取表格再进行数据格式的转换;也可以是从第二步数据获(接口方式)取得到的非结构化(json)数据中还原为表格再处理。
我们假设为第二种(更有通用性)
从view13中获取数据,再转为df。df是可以直接转为h5元素的。
@app1.route('/view14/', methods=['GET', 'POST'])
def view14():
# 1 获取url数据
url = 'http://localhost:5000' + url_for('app1.view13')
resp = req.get(url)
input_dict = json.loads(resp.text)
input_df = pd.DataFrame(input_dict['data'])
return input_df.to_html()
我们使用函数将df默认的表格加上我们制定的元素id和样式。将上面的视图函数略作修改
# 替换h5元素属性
def a_replace_h5attr(para_dict):
res = ''
for k in para_dict.keys():
res += ''' %s="%s" ''' %(k, para_dict[k])
return res
# 获取接口数据
@app1.route('/view14/', methods=['GET', 'POST'])
def view14():
# 1 获取url数据
url = 'http://localhost:5000' + url_for('app1.view13')
resp = req.get(url)
input_dict = json.loads(resp.text)
input_df = pd.DataFrame(input_dict['data'])
print(input_df)
# return jsonify(input_dict)
para_dict = {'id':'table1', 'class':'display'}
html1 = input_df.to_html().replace('''class="dataframe"''', a_replace_h5attr(para_dict))
return html1
在浏览器中可以看到table元素的id和样式已经改过来了。
<table border="1" id="table1" class="display" >
<thead>
<tr style="text-align: right;">
<th></th>
<th>age</th>
<th>name</th>
<th>status</th>
<th>title</th>
...
4 Ajax,DataTables:获取到数据后将数据灌入,使用DataTables格式化
ajax成功后页面有了一个table元素,使用其元素id进行datatables转换就可以了。同时将第一列空元素用checkbox
填充,以便后续的选择。
$(function(){
// 1 载入后的请求数据
data_url = '/read_mongo/view14/'
para_data = {}
para_data['a'] = 'test'
para_data_str = JSON.stringify(para_data)
$.ajax({
type:'POST',
url: data_url,
data: para_data_str,
timeout:3000, //超时毫秒
processData:false,
contentType:'application/json',
// 这种要用get_data加json.loads去获取contentType:'application/x-www-form-urlencoded',
//datatype:'json',
success:function(res_data){
table_h5 = res_data.data
$('#row1col1').empty();
$('#row1col1').html(table_h5)
$('#table1').DataTable();
// 2 格式化
$('#table1').find('tbody').find('tr').each(function(){
// 2.1给第一列增加checkbox项
$(this).children('td:eq(0)').html('<input type="checkbox">')
// 2.2将状态数字用h5元素代替
td4_val = $(this).children('td:eq(4)').text()
$(this).children('td:eq(4)').html(vmap(td4_val))
})
},
error:function(){}
})
5 JQuery格式化:使用Jquery对某些列的格式进行处理,展示起来更方便
需要对哪些列进行格式化,就在外面自定义好函数
// 根据不同的值,使用不同的h5替代
function vmap(val){
// val1 = Number(val)
// alert(val1)
val1 = val
if(val1==1){
res = '<span class="text-success"><i class="fa fa-check-circle" aria-hidden="true"></i></span>'
}
else{
res = '<span class="text-danger"><i class="fa fa-info-circle" aria-hidden="true"></i></span>'
}
return res
}
6 Datatables:用户使用表格快速搜索,浏览
主要利用datatables自带的内容模糊搜索,排序以及分页。
7 模态框:用户通过模态框进行表格内容的修改
有部分js函数看起来比较碎,我就不放上面了。表格的操作分为增、删、改三块,再加上批量提交给后台,因此一共有四个操作。以下代码增加了4个控制按钮:
...
<!-- 空白行作为间隔 -->
<div class="m-5"></div>
<!-- row1 -->
<div class="row" >
<div class="col" id='row1col1'>
<!-- ======================> 按钮组 -->
<div class="btn-group operation">
<button id="btn_add" type="button" class="btn btn-primary mx-2" data-toggle="modal">
<!-- bootstrap4已经不默认提供图标了,而是分开了,需要在别的地方下载,并使用方法也有所变化 -->
<i class="fa fa-plus" aria-hidden="true">新增</i>
</button>
<button id="btn_edit" type="button" class="btn btn-info mx-2">
<i class="fa fa-pencil" aria-hidden="true">修改</i>
</button>
<button id="btn_delete" type="button" class="btn btn-danger mx-2">
<i class="fa fa-remove" aria-hidden="true">删除</i>
</button>
<button id="btn_submmit" type="button" class="btn btn-success mx-2">
<i class="fa fa-paper-plane" aria-hidden="true">批量提交</i>
</button>
</div>
<!-- ======================> 新增模态框 -->
{{add_modal|safe}}
</div>
</div>
...
新增行的模态框没有直接放在里面,一方面是因为代码比较多,另一方面具体的表单区域可以增加不同的行。
新增部分的模态框如下,使用其中的一段jinja语句来动态增加表单区域:
<!-- 点击添加按钮的时候弹出的就是模态框,其本身看起来像一个网页 modal fade -> modal-dialog -> modal-content -> modal-header | ->addBookModal - footer -->
<div class="modal fade" id="addRow" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<!-- 直接用css来控制关闭模态框 -->
<button type="button" class="close" data-dismiss="modal"><i class="fa fa-window-close" aria-hidden="true"> 添加数据</i></button>
</h5>
</div>
<div id="addRowModal" class="modal-body">
<div class="form-horizontal">
{%for k,v in var_dict.items()%}
<div class="form-group">
<label for="{{k|e}}" class="col-sm-2 control-label">{{v|e}}:*</label>
<div class="col-sm-10">
<input class="form-control" id="{{k|e}}" type="text">
</div>
</div>
{% endfor%}
</div>
</div>
<div class="modal-footer">
<div class="center-block">
<button id="cancelAdd" type="button" class="btn btn-warning" data-dismiss="modal">取消</button>
<button id="addRowsInfo" type="button" class="btn btn-success" data-dismiss="modal">保存</button>
</div>
</div>
</div>
</div>
</div>
8 Ajax异步提交:使用Ajax提交更改(选定行)
点击时使用ajax将数据提交,同时可以声明在接受到数据后如何处理(数据库接受更改后会返回数据,由js再渲染到表格里)
function submit_checked(table_id){
var submit_list = []
$(table_id).find('input[type="checkbox"]:checked').each(function(){
var curTr = $(this).closest('tr')
tem_list = []
for(i=1;i<curTr.children('td').length;i++){
tem_list.push($(curTr).children('td').eq(i).text())
}
submit_list.push(tem_list)
// alert($(curTr).children('td:eq(3)').text())
})
// 准备提交
summit_url = '/read_mongo/view16/'
para_dict = {}
para_dict['data'] = submit_list
para_dict_str = JSON.stringify(para_dict)
$.ajax({
type:'POST',
url: summit_url,
data: para_dict_str,
timeout:3000, //超时毫秒
processData:false,
contentType:'application/json',
success:function(){},
error:function(){}
})
}
9 Flask处理:Flask获取更改的数据,并处理为合适的格式
一种是新增,一种是更新,两种方法。对于存mongo来说,这样应该就ok了。(不设置删的操作,而是改为使用is_enable字段,所以删也是一种更新)
@app1.route('/view16/', methods=['GET', 'POST'])
def view16():
para_dict = request.get_json()
print(para_dict)
# ===============>新增方法
# 将其塑造为df
data_df = pd.DataFrame(para_dict['data'], columns = ['tem_row_id','age', 'name', 'status','title'])
print(data_df)
# 选取必要的部分转为json
# data_json = data_df[['tem_row_id','name','age','title']].to_json(orient='records')
# data_json1 = json.loads(data_json)
# print(data_json1)
# save_many_recs(data_json1)
# ===============>键值匹配的更新方法
match_key_list = data_df[['name']].to_json(orient='records')
match_val_list = data_df[['tem_row_id', 'age', 'status','title']].to_json(orient='records')
match_key_list1 = json.loads(match_key_list)
match_val_list1 = json.loads(match_val_list)
print(match_key_list1)
print(match_val_list1)
update_many_recs_with_key(match_key_list1, match_val_list1)
return jsonify(para_dict)
以下是存库的两个函数
# 存数据库
def save_many_recs(data_dict_list):
with MongoClient(mongo_cfg['host'], mongo_cfg['port']) as conn:
db = conn.admin
db.authenticate(mongo_cfg['user'], mongo_cfg['pwd'])
db = conn[mongo_cfg['source_db_name']]
collection = db[mongo_cfg['source_collect_name']]
collection.insert_many(data_dict_list)
# 根据键值更新多个
def update_many_recs_with_key(key_list, val_list):
with MongoClient(mongo_cfg['host'], mongo_cfg['port']) as conn:
db = conn.admin
db.authenticate(mongo_cfg['user'], mongo_cfg['pwd'])
db = conn[mongo_cfg['source_db_name']]
collection = db[mongo_cfg['source_collect_name']]
for i in range(len(key_list)):
collection.find_one_and_update(key_list[i],{'$set':val_list[i]})
10 Flask写入进行写入后返回更改的数据,将这部分数据送回(而不是整个表格)进行异步刷新
这部分略,因为要结合实际的应用去写,没有固定的格式。
Next
- 1 构建服务,以表编辑向mongo传递数据,这部分数据需要符合构建图的规范。
标签:DataTables,Python,cfg,name,Flask,json,dict,data,mongo 来源: https://blog.csdn.net/yukai08008/article/details/111868205