Django实现自动发布(2视图-服务管理)
通常页面要能对资源进行增删改查,对应http的 POST、DELETE、UPDATE、GET
页面显示使用了layui,而layui的表格有自己的数据获取方式,所以我们的视图要做一些调整,不使用后端渲染,只返回数据
具体的实现是页面点击按钮或者导航后,服务端渲染一个空的页面,剩下的数据由页面的js驱动获取。
返回空页面的视图:
from django.views import generic
class ServicePageView(generic.ListView):
template_name = 'microservice/service.html'
def get_queryset(self):
pass
对应的urls:
url(r'^home/$', views.ServicePageView.as_view()),
空页面模板,这里的空指的是内容为空,模板还是有js的,
templates/microservice/service.html:
<table class="layui-hide" id="service-list-table" lay-filter="tableEvent"></table>
<script src="/static/js/layui/layui.js"></script>
<link rel="stylesheet" type="text/css" href="/static/js/layui/css/layui.css" />
<script>
layui.use(['laypage', 'layer', 'form', 'table'], function () {
var laypage = layui.laypage //分页
, layer = layui.layer //弹层
, table = layui.table
;
//执行一个 table 实例
table.render({
elem: '#service-list-table'
, url: '{% url "api_microservice" %}' //数据接口
, cellMinWidth: 80 //全局定义常规单元格的最小宽度,layui 2.2.1 新增
,autoSort: false //禁用前端自动排序
, page: {
curr: 1
, limit: 20
, limits: [20, 30, 40, 50, 100]
, layout: ['count', 'prev', 'page', 'next', 'limit', 'skip']
, jump: function (obj) {
console.log(obj)
}
}
, cols: [[ //表头
{field: 'name', title: 'service名称', sort: true, minWidth: 120}
, {field: 'language', title: '语言类型', width: 100}
, {field: 'description', title: '描述', minWidth: 150}
]]
, id: 'serviceListTable'
});
/*
* 表格搜索与重载
*/
var $ = layui.$, active = {
reload: handleReloadTable
};
// 重载
function handleReloadTable(){
$.ajax({
url: '{% url "api_microservice" %}',
type: "GET",
dataType: "json",
}).done(result => {
table.reload('serviceListTable', {
page: {
curr: 1 //重新从第 1 页开始
}
});
}).fail((xhr, status, error) => {
})
}
</script>
服务管理视图
RESTful风格的api通常设计如下:
GET api/resources 返回数据列表
POST api/resources 则是创建一条记录
UPDATE api/resources/{id} 更新一条记录
DELETE api/resources/{id} 删除一条记录
将获取、创建的方法放在一个视图,修改、删除是另外一个视图
查找
from django.http import JsonResponse
from django.core.paginator import Paginator
from django.utils.timezone import utc, localtime
from microservice.models import *
class ServiceApi(generic.View):
"""
服务管理操作 获取列表、添加
"""
def get(self, request):
query = request.GET
page = query.get('page', 1)
limit = query.get('limit', 20)
services = Service.objects.select_related('updated_by')
paginator = Paginator(services, limit)
pdata = paginator.page(page)
data = [{
'id': item.id,
'name': item.name,
'description': item.description,
'language': item.language,
'build_orig': item.build_orig,
'build_url': item.build_url,
'updated_by': item.updated_by.username,
'updated': localtime(item.updated).strftime('%Y-%m-%d %H:%M:%S %Z'),
} for item in pdata]
return JsonResponse({
'data': data,
'count': services.count(),
'code': 0
})
对应的urls:
url(r'^microservice/$', views.ServiceApi.as_view(), name='api_microservice'),
使用 python manage.py shell
打开控制台,新建几个服务,然后启动django,打开 http://127.0.0.1:8000/home/ ,就能看到页面如下:
创建服务
通过该页面只能进行服务的查看,接下来添加服务新增功能,在 ServiceApi 里增加 post 方法:
def post(self, request):
d = {}
d['name'] = request.POST.get('name', '')
d['language'] = request.POST.get('language', 'cpp')
d['description'] = request.POST.get('description', '')
d['build_orig'] = request.POST.get('build_orig', 'git')
d['build_url'] = request.POST.get('build_url', '')
if not d['name'] or not d['build_url']:
return JsonResponse({'msg': '必填项不能为空'}, status=417)
try:
Service.objects.get(name=d['name'])
except Service.DoesNotExist:
d['created_by'] = request.user
d['updated_by'] = request.user
service = Service.objects.create(**d)
return JsonResponse({}, status=201)
else:
return JsonResponse({'msg': '该服务已存在'}, status=409)
相应的页面也要做一些修改,以下内容添加到 templates/microservice/service.html 头部
<div class="layui-row">
<div class="layui-col-md2 layui-col-md-offset4">
<button id="create-form-button" class="layui-btn" style="float: right;">
<i class="layui-icon layui-icon-add-1"></i> 添加
</button>
</div>
</div>
<!-- 需要弹出的添加界面 -->
<div class="layui-row" id="service-create" style="display: none;">
<div class="layui-col-md10">
<form id="service-create-form" class="layui-form" lay-filter="create"> <!-- 提示:如果你不想用form,你可以换成div等任何一个普通元素 -->
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 名称</label>
<div class="layui-input-block">
<input type="text"
name="name"
placeholder="请输入服务名称"
autocomplete="off"
class="layui-input"
lay-verify="name"
/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 语言类型</label>
<div class="layui-input-block" >
<input type="radio" lay-filter="create-lauguage" name="language" value="cpp" title="cpp" checked>
<input type="radio" lay-filter="create-lauguage" name="language" value="go" title="go" >
<input type="radio" lay-filter="create-lauguage" name="language" value="other" title="其它" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 构建来源</label>
<div class="layui-input-block">
<input type="radio" name="build_orig" value="git" title="git" checked>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 构建地址</label>
<div class="layui-input-block">
<input type="text"
name="build_url"
placeholder="请输入构建地址"
autocomplete="off"
class="layui-input"
lay-verify="build_url"
/>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea rows="5" name="description" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-primary my-cancel-button">取消</button>
<button class="layui-btn" lay-submit="" lay-filter="create-form">立即提交</button>
</div>
</div>
</form>
</div>
</div>
页面增加相关js代码:
/*
* 监听表格事件
*/
// 排序事件
table.on('sort(tableEvent)', function(obj){
table.reload('serviceListTable', {
initSort: obj //记录初始排序,如果不设的话,将无法标记表头的排序状态。
,where: { //请求参数(注意:这里面的参数可任意定义,并非下面固定的格式)
sort_field: obj.field //排序字段
,order: obj.type //排序方式
}
});
});
table.on('tool(tableEvent)', function (obj) {
//注:tool 是工具条事件名,tableEvent 是 table 原始容器的属性 lay-filter="对应的值"
var data = obj.data //获得当前行数据
, layEvent = obj.event; //获得 lay-event 对应的值
if (layEvent === 'del') {
handleDelete(obj);
} else if (layEvent === 'edit') {
handleModify(obj);
}
});
function handleCreate(layerIdx, postdata) {
var loadingLayerIdx = layer.load(2, {
shade: [0.3]
});
$.ajax({
url: '{% url "api_microservice" %}',
type: "POST",
dataType: "json",
data: postdata,
}).done(result => {
layer.msg('提交成功', {
icon: 1,
time: 1000 //2秒关闭(如果不配置,默认是3秒)
}, function () {
handleReloadTable();
layer.close(layerIdx);
});
}).fail((xhr, status, error) => {
// 移除disabled属性 提交按钮可点击
$('#service-create-form button[lay-submit]').removeClass('layui-btn-disabled');
ajaxErrorHandle(xhr, status, error);
}).always(() => {
// close loading
layer.close(loadingLayerIdx);
})
return false;
}
$('#create-form-button').click(function () {
/* 再弹出添加界面 */
var layerIdx = layer.open({
type: 1,
title: '新增',
area: ["50%", "70%"],
content: $("#service-create").html(),
shadeClose: true, // 点击弹出层的shade可关闭弹出层
success: () => {
// 移除disabled属性
$('#service-create-form button[lay-submit]').removeClass('layui-btn-disabled');
}
});
// 取消时关闭弹出层
$('#service-create-form .my-cancel-button').click(() => {
layer.close(layerIdx);
return false; // 阻止浏览器自动跳转
})
/* 渲染表单 */
form.render(null, 'create');
form.verify({
name: (value) => {
let val = value.trim();
if (!val) {
return '服务名不能为空';
}
if (!/[/^[\w.\-_]{3,40}$/.test(val)) {
return '3~40个字符, 大小写字母数字和下划线小数点'
}
}
, language: (value) => {
let val = value.trim();
if (!val) {
return '不能为空';
}
}
, build_url: (value) => {
let val = value.trim();
if (!val) {
return '不能为空';
} else if (!(val.startsWith('http://') || val.startsWith('https://'))) {
return 'url地址格式不正确,必须以http/https开头';
}
}
});
//监听提交
form.on('submit(create-form)', function (data) {
$(this).addClass('layui-btn-disabled');
handleCreate(layerIdx, data.field);
return false; // 阻止浏览器自动跳转
});
});
修改和删除
视图:
class ServiceManageApi(BaseApiView):
"""
服务管理操作 修改、删除
"""
def post(self, request, pk):
params = request.POST
d = {}
d['description'] = params.get('description', '')
d['language'] = params.get('language', '')
d['build_url'] = params.get('build_url', '')
d['updated_by'] = request.user
d['updated'] = datetime.datetime.utcnow().replace(tzinfo=utc)
try:
Service.objects.filter(pk=pk).update(**d)
except Service.DoesNotExist:
return JsonResponse({'msg': '资源不存在'}, status=404)
return JsonResponse({})
def delete(self, request, pk):
svcs = Service.objects.annotate(num_versions=Count('microserviceversion')).filter(pk=pk)
if not svcs:
return JsonResponse({'msg': '资源不存在'}, status=404)
if len(svcs) > 1:
return JsonResponse({'msg': '数据错误'}, status=417)
if svcs[0].num_versions != 0:
return JsonResponse({'msg': '该服务有关联的版本,不允许删除'}, status=417)
svcs.delete()
return JsonResponse({})
对应的urls:
url(r'^microservice/(?P<pk>[0-9]+)/$', views.ServiceManageApi.as_view(), name='api_microservice_manage'),
在页面table的后面增加操作,支持 修改、删除
<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-xs layui-btn-primary" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<!-- 修改 -->
<div class="layui-row" id="service-modify" style="display: none;">
<div class="layui-col-md10">
<form id="service-modify-form" class="layui-form" lay-filter="modify">
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 名称</label>
<div class="layui-input-block">
<input type="text"
name="name"
placeholder="请输入服务名称"
autocomplete="off"
class="layui-input"
lay-verify="name"
disabled=""
style="background-color: #eee; cursor: not-allowed;"
/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 语言类型</label>
<div class="layui-input-block">
<input type="radio" name="language" value="cpp" title="cpp" disabled>
<input type="radio" name="language" value="go" title="go" disabled>
<input type="radio" name="language" value="other" title="其它" disabled>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label"><i class="layui-icon layui-icon-rate" style="color: red;"></i> 构建地址</label>
<div class="layui-input-block">
<input type="text"
name="build_url"
placeholder="请输入构建地址"
autocomplete="off"
class="layui-input"
lay-verify="build_url"
disabled=""
style="background-color: #eee; cursor: not-allowed;"
/>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<textarea rows="5" name="description" placeholder="请输入内容" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-primary my-cancel-button">取消</button>
<button class="layui-btn" lay-submit="" lay-filter="modify-form">保存修改</button>
</div>
</div>
</form>
</div>
</div>
相应的js:
// 渲染表格的cols里增加
, {fixed: 'right', title: '操作', width: 300, align: 'center', toolbar: '#barDemo'}
/*
* 编辑 删除
*/
function handleDelete(obj) {
layer.confirm('确定删除?删除后不可恢复!', {icon: 0, title:'提示'}, function (index) {
layer.close(index);
var loadingLayerIdx = layer.load(2, {
shade: [0.3]
});
var url = '{% url "api_microservice_manage" 0 %}';
url = url.substr(0, url.lastIndexOf(0));
url += obj.data.id + '/';
$.ajax({
url: url,
type: "DELETE",
}).done(result => {
layer.msg('删除成功', {icon: 1});
obj.del(); //删除对应行(tr)的DOM结构
}).fail((xhr, status, error) => {
layer.msg('删除失败: ' + (xhr.status >= 500 ? '服务器内部错误' : xhr.responseJSON.msg), {icon: 2});
}).always(() => {
layer.close(loadingLayerIdx);
});
})
}
function handleModify(obj) {
/* 弹出修改界面 */
var data = obj.data;
var layerIdx = layer.open({
type: 1, // 0(信息框,默认)1(页面层)2(iframe层)3(加载层)4(tips层)
title: '修改服务',
area: ["50%", "70%"],
content: $("#service-modify").html(),
shadeClose: true, // 点击弹出层的shade可关闭弹出层
success: () => {
// 移除disabled属性 提交按钮可点击
$('#service-modify-form button[lay-submit]').removeClass('layui-btn-disabled');
}
});
// 取消时关闭弹出层
$('#service-modify-form .my-cancel-button').click(() => {
layer.close(layerIdx);
return false; // 阻止浏览器自动跳转
})
//表单初始赋值
form.val('modify', {
"name": data.name // "name": "value"
,"language": data.language
,"build_orig": data.build_orig
,"build_url": data.build_url
,"description": data.description
})
/* 渲染表单 */
form.render(null, 'modify');
form.render('select', 'modify');
form.verify({
build_url: (value) => {
let val = value.trim();
if (!val) {
return '不能为空';
} else if (!val.startsWith('http://') || !val.startsWith('https://')) {
return 'url地址格式不正确,必须以http/https开头';
}
}
});
//监听提交
form.on('submit(modify-form)', function (data) {
$(this).addClass('layui-btn-disabled');
var loadingLayerIdx = layer.load(2, {
shade: [0.3]
});
var url = '{% url "api_microservice_manage" 0 %}';
url = url.substr(0, url.lastIndexOf(0));
url += obj.data.id + '/';
$.ajax({
url: url,
type: "POST",
dataType: "json",
data: data.field,
}).done(result => {
layer.msg('提交成功', {
icon: 1,
time: 1000 //2秒关闭(如果不配置,默认是3秒)
}, function () {
layer.close(layerIdx);
handleReloadTable(false);
});
}).fail((xhr, status, error) => {
// 移除disabled属性 提交按钮可点击
$('#service-modify-form button[lay-submit]').removeClass('layui-btn-disabled');
layer.msg('提交失败: ' + (xhr.status >= 500 ? '服务器内部错误' : xhr.responseJSON.msg), {icon: 2});
}).always(() => {
layer.close(loadingLayerIdx);
})
return false;
});
}
页面效果图:
相关的代码在 这里