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;
});
}
页面效果图:
相关的代码在 这里
Django实现自动发布(2视图-服务管理)的更多相关文章
- Django实现自动发布(2视图-服务版本查找和新增)
前面做好了服务的管理,接下来是服务版本的管理,和服务类似,版本也有增删改查.先在服务的管理页面做一个入口,如下图: 需要在上一步的服务管理页面增加按钮.按钮方法,点击按钮跳转时要打开一个新的页面,所以 ...
- Django实现自动发布(2视图-任务接收)
上一篇服务版本的新增,是通过触发 gitlab 任务来实现的,那么如何得到任务的最终状态呢? 好在 gitlab 为我们提供了webhook,也就是消息钩子,可以发送pipeline消息到我们指定的地 ...
- 解决GP服务产生的结果无法自动发布为地图服务的问题
在ArcGIS for Javascript API或REST API调用GP服务时,常常会遇到这样一种情况:GP服务运行以后,执行成功,也能够生成结果,然而结果并没有直接产生动态的地图服务供API调 ...
- Django实现自动发布(1数据模型)
公司成立之初,业务量较小,一个程序包揽了所有的业务逻辑,此时服务器数量少,上线简单,基本开发-测试-上线都是由开发人员完成. 随着业务量逐渐上升,功能增多,代码量增大,而单一功能上线需要重新编译整个程 ...
- Django实现自动发布(3发布-升级和回退)
发布实际上就是将服务的某个版本和一台主机关联,我用一张表(MicroServiceInstance)记录了主机id.服务id.版本id,目前一台主机只能部署一个版本,所以主机id和服务id要做联合索引 ...
- Django实现自动发布(3发布-安装)
相对于服务的升级.回退,新部署一个服务要复杂一些,要满足以下要求: 已经运行了服务实例的主机不能重复部署 进程启动需要的配置文件要先同步到主机上 之前的升级.回退都是指进程的操作,不涉及配置文件的变更 ...
- 使用Jenkins自动发布Windows服务项目
不同于发布Web项目,自动发布Windows服务项目需要解决以下几个问题: 如何远程停止和开启服务?需要在发布前停止服务,在发布完成后开启服务. 如何上传编译文件到目标服务器? 问题1:如何远程停止和 ...
- GeoServer自动发布地图服务
1 NetCDF气象文件自动发布案例 GeoServer是一个地理服务器,提供了管理页面进行服务发布,样式,切片,图层预览等一系列操作,但是手动进行页面配置有时并不满足业务需求,所以GeoServer ...
- Rainbond v5.1.2发布,微服务架构应用便捷管理和交付
Rainbond v5.1.2发布,微服务架构应用便捷管理和交付 Rainbond是开源的企业应用云操作系统,支撑企业应用的开发.架构.交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资 ...
随机推荐
- Guava入门
其实我用guava差不多大半年时间了,发现guava真的特别好用,又会使代码变得很简洁,最近又系统的学习了一下,大致讲一下
- android shap画圆(空心圆、实心圆)
实心圆: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android=" ...
- Android Scroller解析
作用 这个类封装了滚动操作,如帮我们处理手指抬起来时候的滑动操作.与ViewGroup的scrollTo(),scrollBy()的生硬式移动,Scroller提供了一个更加柔和的移动效果.Scrol ...
- Django之auth认证
Auth模块是Django自带的用户认证模块: 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的 ...
- Odoo日历视图详解
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826148.html 一:日历视图定义 根元素为<calendar>. 主要的属性有: ...
- 一个兼容IE7\IE8,H5的多功能视频播放器,H5视频播放器兼容Flash视频播放器
这里记录一个视频播放器,免费可适当修改:名称:ckplayer视频播放器(免费) 官网地址:http://www.ckplayer.com/ 下载地址:http://www.ckplayer.com/ ...
- DRF序列化和反序列化(二:ModelSerializer)
一: rest_framework 中 serializers.Serializer的不方便之处(以下简称Serializer) a:需要定义每一个字段,并且这个字段是和models字段及其类似. b ...
- django小知识(1)
聚合查询 关键字:aggregate from django.db.models import Max,Min,Count,Sum,Avg 分组查询 关键字:annotate 1.最简单的规律 mod ...
- 项目Beta冲刺(2/7)(追光的人)(2019.5.24)
所属课程 软件工程1916 作业要求 Beta冲刺博客汇总 团队名称 追光的人 作业目标 描述Beta冲刺每日的scrum和PM报告两部分 队员学号 队员博客 221600219 小墨 https:/ ...
- 使用 Express Generator快速创建Express应用
全局安装express-generator npm install express-generator -g 根据需求生成自己需要的模板 生成ejs模板:express demo --view=ejs ...