zoukankan      html  css  js  c++  java
  • 代码发布4 发布任务表创建, 发布任务前端页面搭建, 发布任务页面动态展示流程, ModelForm(exclude, 重写save, 全局钩子)

    发布任务表创建

    class DeployTask(models.Model):
        """发布任务单
        项目主键            版本号
            1               v1
            1               v2
            1               v3
            2               v1
            2               v2
        """
        # uid = luffycity-test-v1-20202020111  项目-环境-版本-日期
        uid = models.CharField(verbose_name='唯一标识',max_length=128)
        project = models.ForeignKey(verbose_name='项目',to='Project')
        tag = models.CharField(verbose_name='版本',max_length=32)
    
        status_choices = (
            (1,'待发布'),
            (2,'发布中'),
            (3,'成功'),
            (4,'失败'),
        )
        status = models.IntegerField(verbose_name='状态',choices=status_choices,default=1)
    
        """我们在发布任务的时候 我开设了几个钩子脚本节点 可以支持发布者在发布流程的某个阶段执行额外的操作"""
        # 钩子节点的个数是不一定的 结合自己的公司需求
        before_download_script = models.TextField(verbose_name='下载前脚本', null=True, blank=True)
        after_download_script = models.TextField(verbose_name='下载后脚本', null=True, blank=True)
        before_deploy_script = models.TextField(verbose_name='发布前脚本', null=True, blank=True)
        after_deploy_script = models.TextField(verbose_name='发布后脚本', null=True, blank=True)

    发布任务逻辑处理

    不直接写增删改查

    而是在项目的查看列表中新增一个发布记录字典,点击该字段查看当前项目所对应的发布记录,然后在查看页面书写增删改查,这样做的目的就是可以直接针对对应的项目,无需用户自己选择

    对于发布任务单的添加页面,不再使用公共的form.html而是自己单独开设一个,因为需要做额外的扩展

    出于用户体验的角度考虑,我们将添加任务单的界面大致分为三块区域

    • 基本信息展示区

      <table class="table table-hover table-striped">
            <tbody>
            <tr>
                <td>项目名称:{{ project_obj.title }}</td>
                <td>环境:{{ project_obj.get_env_display }}</td>
            </tr>
            <tr>
                <td colspan="2">仓库地址:{{ project_obj.repo }}</td>
            </tr>
            <tr>
                <td colspan="2">线上路径:{{ project_obj.path }}</td>
            </tr>
            <tr>
                <td colspan="2">
                    <div>关联服务器</div>
                    <ul>
                        {% for server_obj in project_obj.servers.all %}
                            <li>{{ server_obj.hostname }}</li>
                        {% endfor %}
                    </ul>
                </td>
            </tr>
            </tbody>
        </table>
    • 基本配置
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"><span class="glyphicon glyphicon-cog"></span>基本配置</h3>
                </div>
                <div class="panel-body form-horizontal">
                    <label for="{{ form_obj.tag.id_for_label }}"
                           class="col-md-2 control-label">{{ form_obj.tag.label }}</label>
                    <div class="col-md-10">
                        {{ form_obj.tag }}
                        <span style="color: red">{{ form_obj.tag.errors.0 }}</span>
                    </div>
                </div>
            </div>
    • 钩子脚本渲染

    初步

    <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"><span class="glyphicon glyphicon-th-list"></span>发布流程&钩子</h3>
                </div>
                <div class="panel-body form-horizontal">
                    {#              1 简易流程图区域#}
                    <div class="outline">
                        <div class="series">
                            <div class="module clearfix">
                                <div class="item left">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="down">01 开始</a>
                                    </div>
                                </div>
    
                                <div class="item left active">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="up">02 下载前</a>
                                    </div>
                                </div>
                                <div class="item left">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="down">03 下载代码</a>
                                    </div>
                                </div>
    
                                <div class="item left active">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="up">04 下载后</a>
                                    </div>
                                </div>
    
                                <div class="item left">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="down">05 打包上传</a>
                                    </div>
                                </div>
    
                                <div class="item left active">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="up">06 发布前</a>
                                    </div>
                                </div>
    
                                <div class="item left">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="down">07 发布</a>
                                    </div>
                                </div>
    
                                <div class="item left active">
                                    <div class="line">
                                        <hr>
                                    </div>
                                    <div class="icon">
                                        <span class="glyphicon glyphicon-record" aria-hidden="true"></span>
                                        <a class="up">08 发布后</a>
                                    </div>
                                </div>
                                <div class="item left">
                                    <div class="line">
                                        <hr>
                                    </div>
                                </div>
    
                            </div>
                        </div>
                    </div>
                    {#              2 钩子脚本渲染区域#}
                    <div class="hooks">
                        <div class="col-md-6">
                            <div class="panel panel-default">
                                <div class="panel-heading">
                                    <h3 class="panel-title">02 下载前</h3>
                                </div>
                                <div class="panel-body">
                                    {{ form_obj.before_download_script }}
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="panel panel-default">
                                <div class="panel-heading">
                                    <h3 class="panel-title">04 下载后</h3>
                                </div>
                                <div class="panel-body">
                                    {{ form_obj.after_download_script }}
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="panel panel-default">
                                <div class="panel-heading">
                                    <h3 class="panel-title">06 发布前</h3>
                                </div>
                                <div class="panel-body">
                                    {{ form_obj.before_deploy_script }}
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="panel panel-default">
                                <div class="panel-heading">
                                    <h3 class="panel-title">08 发布后</h3>
                                </div>
                                <div class="panel-body">
                                    {{ form_obj.after_deploy_script }}
                                </div>
                            </div>
                        </div>
    
    
                    </div>
                </div>
            </div>

    动态生成唯一标识

        def create_uid(self):
            # 生成唯一标识
            # luffycity-test-v1-20202020111  如果有项目对象会非常简单
            title = self.project_obj.title
            env = self.project_obj.env
            tag = self.cleaned_data.get('tag')
            current_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
            return "{0}-{1}-{2}-{3}".format(title,env,tag,current_time)

    钩子脚本功能完善

    实现用户定义的钩子脚本可以保存并且多次使用

    这样做的好处在于,你这个软件使用的时间越久,内部保存的脚本越多,后续使用人员就可以方便的调用和修改之前的脚本内容从而减少工作量

    我们前面写的taskmodelform中,没有下拉框,checkbox和模版文本框

    我们需要去taskmodelform中额外的定义这些字段

    <div class="panel-body form-horizontal">
                                    <div class="form-group">
                                        <div class="col-md-12">
                                            {{ form_obj.after_download_select }}
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-md-12">
                                            {{ form_obj.after_download_script }}
                                            <span style="color: red">{{ form_obj.after_download_script.errors.0 }}</span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <div class="col-md-3">
                                            <div class="checkbox">
                                                <label>{{ form_obj.after_download_template }}保存为模版</label>
                                            </div>
    
                                        </div>
                                        <div class="col-md-9">
                                            {{ form_obj.after_download_title }}
                                            <span style="color: red">{{ form_obj.after_download_title.errors.0 }}</span>
                                        </div>
                                    </div>
                                </div>

    下拉框数据初始化

        def __init__(self,project_obj,*args,**kwargs):
            """当你不知道一个函数或者方法都有哪些参数的时候 就用*args,**kwargs哥俩"""
            super().__init__(*args,**kwargs)
            # 给对象添加额外的属性
            self.project_obj = project_obj
    
            # 给下拉框添加数据  <option value="0">请选择</option>   .choices使该字段前端变为下拉框
            self.fields['before_download_select'].choices = [(0,'请选择')]
            self.fields['after_download_select'].choices = [(0,'请选择')]
            self.fields['before_deploy_select'].choices = [(0,'请选择')]
            self.fields['after_deploy_select'].choices = [(0,'请选择')]

    钩子模版的保存及前端动态展示的功能

    下拉框中点击脚本名称就会去后端请求对于的脚本内容

    也就意味着我们应该开设一张专门用来存储钩子脚本内容的表

    class HookTemplate(models.Model):
        """钩子模版"""
        title = models.CharField(verbose_name='标题',max_length=64)
        content = models.TextField(verbose_name='脚本内容')
    
        # 针对不同的脚本节点做对应的钩子标记
        hook_type_choices = (
            (2,'下载前'),
            (4,'下载后'),
            (6,'发布前'),
            (8,'发布后'),
        )
        hook_type = models.IntegerField(verbose_name='钩子类型',choices=hook_type_choices)

    当用户点击了checkbox按钮的时候,就应该去操作上面的表来存储脚本内容

    我们在后端只需要判断用户是否点击了,再做相应的处理即可!!!

    # 判断用户是否点击了checkbox
            if self.cleaned_data.get('before_download_template'):
                # 获取脚本文件名和脚本内容
                title = self.cleaned_data.get('before_download_title')
                content = self.cleaned_data.get('before_download_script')
                # 写入数据库
                models.HookTemplate.objects.create(
                    title=title,
                    content=content,
                    hook_type=2
                )

    下拉框如何展示对应的钩子脚本文件名

    # 给下拉框添加数据  <option value="0">请选择</option>
            before_download = [(0, '请选择')]
            extra_choices = models.HookTemplate.objects.filter(hook_type=2).values_list('pk','title')  # [(),()]
            before_download.extend(extra_choices)
            self.fields['before_download_select'].choices = before_download

    选择钩子脚本文件名展示对应的脚本内容

    利用事件操作(文本域改变事件)和ajax实现动态展示

    # 文本域改变事件 change事件
    <script>
            // 给所有的下拉框绑定文本域改变事件
            // 避免麻烦 直接使用范围查找
            $('.hooks').find('select').change(function () {
                var that = $(this);
                // 发送ajax请求获取脚本内容
                $.ajax({
                    url:'/hook/template/' + that.val() + '/',
                    type:'get',
                    dataType:'JSON',
                    success:function (args) {
                        if(args.status){
                            {#alert(args.content)#}
                            // 将脚本内容通过DOM操作渲染到对应的文本框中
                            that.parent().parent().next().find('textarea').val(args.content)
                        }
                    }
    
                })
            })
        </script>

    功能完善

    当用户点击了保存模版,但是没有填写脚本内容的时候,应该作出相应的提示信息

    重写clean方法(局部钩子 全局钩子)

     def clean(self):
            """全局钩子 校验用户钩子脚本标题是否填写"""
            if self.cleaned_data.get('before_download_template'):
                title = self.cleaned_data.get('before_download_title')
                if not title:
                    # 添加报错信息
                    self.add_error('before_download_title','请输入模版名称')
    
            if self.cleaned_data.get('after_download_template'):
                title = self.cleaned_data.get('after_download_title')
                if not title:
                    # 添加报错信息
                    self.add_error('after_download_title','请输入模版名称')
    
            if self.cleaned_data.get('before_deploy_template'):
                title = self.cleaned_data.get('before_deploy_title')
                if not title:
                    # 添加报错信息
                    self.add_error('before_deploy_title','请输入模版名称')
    
            if self.cleaned_data.get('after_deploy_template'):
                title = self.cleaned_data.get('after_deploy_title')
                if not title:
                    # 添加报错信息
                    self.add_error('after_deploy_title','请输入模版名称')

    提示信息没有足够的位置展示,会出现页面错乱的情况

    提前给提示信息预留位置

                                    <div class="form-group" style="height: 60px">
                                        <div class="col-md-3">
                                            <div class="checkbox">
                                                <label>{{ form_obj.after_deploy_template }}保存为模版</label>
                                            </div>
    
                                        </div>
                                        <div class="col-md-9">
                                            {{ form_obj.after_deploy_title }}
                                            <span style="color: red">{{ form_obj.after_deploy_title.errors.0 }}</span>
                                        </div>
                                    </div>

     

    ModelForm的exclude

    from django.forms import ModelForm
    class TaskModeForm(ModelForm):
        ...
        class Meta:
            model = models.DeployTask
            fields = '__all__'
            # 指定字段不展示到前端页面
            exclude = ['uid','project','status']
    
        def save(self, commit=True):
            self.instance.uid = self.create_uid()   # .instance就是当前任务model的对象,不加为ModelForm对象|补上project_id,uid属性
            self.instance.project_id = self.project_obj.pk
            # 调用父类的save方法保存数据
            super().save(commit)
    
            # 判断用户是否点击了checkbox
            if self.cleaned_data.get('before_download_template'):
                # 获取脚本文件名和脚本内容
                title = self.cleaned_data.get('before_download_title')
                content = self.cleaned_data.get('before_download_script')
                # 写入数据库
                models.HookTemplate.objects.create(
                    title = title,
                    content = content,
                    hook_type=2
                )
    
        def clean(self):
            '''全局钩子 校验用户钩子脚本标题是否填写'''
            if self.cleaned_data.get('before_download_template'):
                title = self.cleaned_data.get('before_download_title')
                if not title:
                    # 添加报错信息
                    self.add_error('before_download_title','请输入模板名称')
  • 相关阅读:
    CROC 2016
    CROC 2016
    CROC 2016
    IndiaHacks 2016
    IndiaHacks 2016
    @JsonProperty的使用
    JDK8新特性:函数式接口@FunctionalInterface的使用说明
    cannot nest '/dubboService/src/main/resources' inside '/dubboService/src/main' .To enable the nesting exclude '/resources' from '/dubboService/src/main'
    【转】关于BeanUtils.copyProperties的用法和优缺点
    JAXB--@XmlElementWrapper注解(二)
  • 原文地址:https://www.cnblogs.com/ludingchao/p/12716098.html
Copyright © 2011-2022 走看看