• 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: '语言类型',  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: '操作',  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;
            });
          }
    

    页面效果图:

    相关的代码在 这里

  • 相关阅读:
    HNOI2019 JOJO
    十二省联考2019 骗分过样例
    十二省联考2019 皮配
    十二省联考2019 字符串问题
    十二省联考2019 春节十二响
    十二省联考2019 异或粽子
    HNOI2019 白兔之舞 dance
    HNOI2019 多边形 polygon
    HNOI2019 鱼 fish
    P4770 [NOI2018]你的名字
  • 原文地址:https://www.cnblogs.com/wbjxxzx/p/11967023.html
Copyright © 2020-2023  润新知