代码发布02
目录
modelform的使用
from django.forms import ModelForm
class BaseModelForm(ModelForm):
class Meta:
model = models.Userinfo
fields = '__all__'
# exclude = ['uid'] # 排除在外
# 展示标签
form_obj = BaseModelForm()
# 校验数据
form_obj = BaseModelForm(data=request.POST)
form_obj.is_valid()
# 新增数据
form_obj.save()
# 展示待编辑数据
form_obj = BaseModelForm(instance=edit_obj)
# 编辑数据
form_obj = BaseModelForm(data=request.POST,instance=edit_obj)
form_obj.is_valid()
form_obj.save()
"""新增和编辑调用的都是save方法,两者通过instance来区分"""
删除二次确认及页面刷新
二次确认你可以直接使用原生的BOM操作,也可以借助于第三方插件是页面更加的好看,针对删除之后页面的刷新,最好不要直接刷新因为那样没有考虑到分页的情况,简易你使用DOM操作的方式刷新页面,能够保证在分页的情况下也能合理展示
发布任务表创建
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>
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>
选择了IT,必定终身学习