通常页面要能对资源进行增删改查,对应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视图-服务管理)的更多相关文章

  1. Django实现自动发布(2视图-服务版本查找和新增)

    前面做好了服务的管理,接下来是服务版本的管理,和服务类似,版本也有增删改查.先在服务的管理页面做一个入口,如下图: 需要在上一步的服务管理页面增加按钮.按钮方法,点击按钮跳转时要打开一个新的页面,所以 ...

  2. Django实现自动发布(2视图-任务接收)

    上一篇服务版本的新增,是通过触发 gitlab 任务来实现的,那么如何得到任务的最终状态呢? 好在 gitlab 为我们提供了webhook,也就是消息钩子,可以发送pipeline消息到我们指定的地 ...

  3. 解决GP服务产生的结果无法自动发布为地图服务的问题

    在ArcGIS for Javascript API或REST API调用GP服务时,常常会遇到这样一种情况:GP服务运行以后,执行成功,也能够生成结果,然而结果并没有直接产生动态的地图服务供API调 ...

  4. Django实现自动发布(1数据模型)

    公司成立之初,业务量较小,一个程序包揽了所有的业务逻辑,此时服务器数量少,上线简单,基本开发-测试-上线都是由开发人员完成. 随着业务量逐渐上升,功能增多,代码量增大,而单一功能上线需要重新编译整个程 ...

  5. Django实现自动发布(3发布-升级和回退)

    发布实际上就是将服务的某个版本和一台主机关联,我用一张表(MicroServiceInstance)记录了主机id.服务id.版本id,目前一台主机只能部署一个版本,所以主机id和服务id要做联合索引 ...

  6. Django实现自动发布(3发布-安装)

    相对于服务的升级.回退,新部署一个服务要复杂一些,要满足以下要求: 已经运行了服务实例的主机不能重复部署 进程启动需要的配置文件要先同步到主机上 之前的升级.回退都是指进程的操作,不涉及配置文件的变更 ...

  7. 使用Jenkins自动发布Windows服务项目

    不同于发布Web项目,自动发布Windows服务项目需要解决以下几个问题: 如何远程停止和开启服务?需要在发布前停止服务,在发布完成后开启服务. 如何上传编译文件到目标服务器? 问题1:如何远程停止和 ...

  8. GeoServer自动发布地图服务

    1 NetCDF气象文件自动发布案例 GeoServer是一个地理服务器,提供了管理页面进行服务发布,样式,切片,图层预览等一系列操作,但是手动进行页面配置有时并不满足业务需求,所以GeoServer ...

  9. Rainbond v5.1.2发布,微服务架构应用便捷管理和交付

    Rainbond v5.1.2发布,微服务架构应用便捷管理和交付 Rainbond是开源的企业应用云操作系统,支撑企业应用的开发.架构.交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资 ...

随机推荐

  1. Cat搭建遇坑记

    1. Cat搭建遇坑记 1.1. 报错 服务端启动 Unable to get component: class com.dianping.cat.analysis.TcpSocketReceiver ...

  2. 好用的随查工具Dash

    1. 好用的随查工具Dash 1.1. mac版本推荐 推荐一个很好用的工具,名字就叫Dash,当然缺点就是只有英文版,中文版可能也有但更新肯定不如英文及时 可以自由下载各种文档,比如下面的这图,左侧 ...

  3. JavaScript 函数(一)

    一.函数概述 1.概述 把一段相对独立的具有特定功能的代码块封装起来,形成一个独立实体,就是函数,起个名字(函数名),在后续开发中可以反复调用函数的作用就是封装一段代码,将来可以重复使用. 二.使用函 ...

  4. 搞不懂JS中赋值·浅拷贝·深拷贝的请看这里

    前言 百科定义:拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这 ...

  5. SpringCloud SpringBoot 前后端分离企业级微服务架构源码赠送

    基于SpringBoot2.x.SpringCloud和SpringCloudAlibaba并采用前后端分离的企业级微服务敏捷开发系统架构.并引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手 ...

  6. Java集合学习(8):LinkedList

    一.概述 LinkedList和ArrayList一样,都实现了List接口,但其内部的数据结构有本质的不同.LinkedList是基于链表实现的(通过名字也能区分开来),所以它的插入和删除操作比Ar ...

  7. Vagrant+VirtualBox虚拟环境

    Vagrant+VirtualBox虚拟环境 VagrantVirtualBox 软件安装 虚拟机基础配置 虚拟机创建 共享目录 配置网络 配置私有网络 配置公有网络 打包box与添加box 打包bo ...

  8. JAVA构造器,重载与重写

    1. java构造器 构造器也叫构造方法(constructor), 用于对象初始化. 构造器是一个创建对象时被自动创建的特殊方法,目的是对象的初始化. 构造器 的名称与类的名称一致. JAVA通过n ...

  9. dfs 解决(隐式)图搜索问题

    132. 单词搜索 II 中文 English 给出一个由小写字母组成的矩阵和一个字典.找出所有同时在字典和矩阵中出现的单词.一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动. ...

  10. Broadcast,Scatter,Gather,Reduce,All-reduce分别是什么?

    Broadcast 看名字就很好理解了,其实就是把同一份数据分发广播给所有人,示意图如下: Scatter 不同于Broadcast, scatter可以将不同数据分发给不同的进程. Gather 这 ...