mysql 钩子程序_20200319 代码发布之任务发布钩子脚本

昨日内容

* ### 代码发布整体工作流程

参考qq截图

代码发布这个功能可以基于很多方式很多语言来实现

我们这里主要用的是python相关的知识点来完成的,大体逻辑流程都是大差不差的

额外补充:p2p(比特流技术),为了减轻服务器的压力,将所有人即是下载者又是资源的上传者

* ### 服务器表的增删改查

我们从头到位将增删改查自己实现了一遍,目的是为了搭建项目增删改查基本业务逻辑,方便后续其他表的操作

#### **modelform使用**

```python

from django.forms import ModelForm

class MyModelForm(ModelForm):

class Meta:

model = models.UserInfo

fields = '__all__' # 前端展示用户表所有的字段

exclude = ['id'] # 排除id字段 不展示到前端

# 渲染标签

form_obj = MyModelForm()

# 校验数据

form_obj = MyModelForm(data=request.POST)

# 新增数据

form_obj.save()

# 编辑数据 渲染标签

form_obj = MyModelForm(instance=edit_obj)

# 修改数据库中的数据

form_obj = MyModelForm(instance=edit_obj,data=request.POST)

form_obj.save()

针对数据的删除功能,一般情况下都需要有一个二次确认的操作

我们是直接利用BOM操作里面的confirm确认框实现的

你还可以借助于第三方插件效果更好一点比如sweetalert插件

### 项目表的增删改查

直接拷贝服务器表所有的代码,修改变量名即可

### 项目优化

将modelform单独放到一个文件夹中

然后再将各个模型表对应的modelform中相同的代码抽取出来形成基类

给项目表再增两个字段

线上项目地址、关联服务器

### 发布任务

由于发布任务是针对项目的,所以为了之后新增任务的时候不需要自己选择项目,所以我们将发布任务做成项目表中的一个字段,点击该字段跳转到该项目对应的所有发布纪录中,之后在该发布纪录页面上开设新增按钮,将当前项目的主键值传递到后端,这样的话新增任务就无需自己选择项目了

今日

发布任务单新增页面

发布流程前端实时展示

任务发布数据

任务列表的展示为了更加人性化,可以增加当前项目的项目名和环境

formmodel

# 剔除参与展示的字段

exclude = ['uid','project','status']

# form_obj.instance 就是当前的数据对象

form_obj.instance.uid = '唯一标识'

# tasklist有名分组需要传递参数,使用reverse反向解析

url = reverse('task_list',args=(project_id,))

return redirect(url)

初步进行封装重写modelform类的save方法,实现数据的添加

class TaskModelForm(BaseModelForm):

class Meta:

model = models.DeployTask

fields = '__all__'

# 剔除参与展示的字段

exclude = ['uid','project','status']

# 利用初始化获取project_id

def __init__(self,project_id,*args,**kwargs):

super().__init__(*args,**kwargs)

self.project_id = project_id

def save(self, commit=True):

# 添加数据 (重写init方法进行数据的获取)

# .instance 就是数据对象本身,重写save进行保存

self.instance.uid = '唯一标识'

self.instance.project_id = self.project_obj.pk

# 调用父类save方法保存数据

super().save(commit=True)

接下来,我们针对任务的添加页面,单独开设一个html (task_form.html)

并在该html页面上划分三块区域展示不同的信息

当前任务关联的项目基本信息展示

基本配置

脚本书写

获取用户输入的数据cleaned_data

tag = self.cleaned_data.get('tag')

钩子脚本展示

d39a98b091555e0c58f45585892ff249.png

8dfd6fe2dfc85c9acc0a90f53f0ed099.png

针对四个钩子脚本,我们想要实现可以保存的功能

# 初始化字段

def __init__(self,project_obj,*args,**kwargs):

super().__init__(*args,**kwargs)

self.project_obj = project_obj

# 初始化选择框内容

self.init_hook()

def init_hook(self):

# 给所有的下拉框先添加一个 请选择选项

# 请选择 (0,'请选择')

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,'请选择')]

生成新字段

需要新添加字段

# 利用之前定义的基类中的`exclude_bootstrap=[]`来控制样式的添加

# checkbox无需添加样式

exclude_bootstrap = [

'before_download_template',

'after_download_template',

'before_deploy_template',

'after_deploy_template'

]

# 自己定义新的字段

# 下拉框 checkbox 文本框

before_download_select = forms.ChoiceField(required=False, label='下载前')

before_download_title = forms.CharField(required=False, label='模板名称')

before_download_template = forms.BooleanField(required=False, widget=forms.CheckboxInput, label='是否保存为模板')

after_download_select = forms.ChoiceField(required=False, label='下载后')

after_download_title = forms.CharField(required=False, label='模板名称')

after_download_template = forms.BooleanField(required=False, widget=forms.CheckboxInput, label='是否保存为模板')

before_deploy_select = forms.ChoiceField(required=False, label='发布前')

before_deploy_title = forms.CharField(required=False, label='模板名称')

before_deploy_template = forms.BooleanField(required=False, widget=forms.CheckboxInput, label='是否保存为模板')

after_deploy_select = forms.ChoiceField(required=False, label='下载后')

after_deploy_title = forms.CharField(required=False, label='模板名称')

after_deploy_template = forms.BooleanField(required=False, widget=forms.CheckboxInput, label='是否保存为模板')

下拉框的展示

初始化选择框内容,是的前端进行展示下拉框内容 choices=

94593cdbce1440a3008645c0171ed5b2.png

# 初始化字段

def __init__(self,project_obj,*args,**kwargs):

super().__init__(*args,**kwargs)

self.project_obj = project_obj

# 初始化选择框内容

self.init_hook()

def init_hook(self):

# 给所有的下拉框先添加一个 请选择选项

# 请选择 (0,'请选择')

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=32)

content = models.TextField(verbose_name='脚本内容')

# 我想实现钩子与钩子之间模版互不影响

hook_type_choices = (

(2,'下载前'),

(4,'下载后'),

(6,'发布前'),

(8,'发布后')

)

hook_type = models.IntegerField(verbose_name='钩子类型',choices=hook_type_choices)

什么时候操作数据表保存数据?

判断用户是否点击了checkbox按钮

在重写的save()方法中判断

def save(self, commit=True):

# 添加数据

self.instance.uid = self.create_uid()

self.instance.project_id = self.project_obj.pk

# 调用父类的save方法保存数据

super().save(commit=True)

# 判断用户是否点击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

)

if self.cleaned_data.get("after_download_template"):

# 获取模版名称

title = self.cleaned_data.get("after_download_title")

# 获取脚本内容

content = self.cleaned_data.get("after_download_script")

# 保存到表中

models.HookTemplate.objects.create(

title=title,

content=content,

hook_type=4

)

if self.cleaned_data.get("before_deploy_template"):

# 获取模版名称

title = self.cleaned_data.get("before_deploy_title")

# 获取脚本内容

content = self.cleaned_data.get("before_deploy_script")

# 保存到表中

models.HookTemplate.objects.create(

title=title,

content=content,

hook_type=6

)

if self.cleaned_data.get("after_deploy_template"):

# 获取模版名称

title = self.cleaned_data.get("after_deploy_title")

# 获取脚本内容

content = self.cleaned_data.get("after_deploy_script")

# 保存到表中

models.HookTemplate.objects.create(

title=title,

content=content,

hook_type=8

)

下拉框显示钩子模板数据

# 给所有的下拉框先添加一个 请选择选项

# 请选择 (0,'请选择')

before_download = [(0,'请选择')]

# 还应该去数据库中查询是否有对应的模版名称

extra_list = models.HookTemplate.objects.filter(hook_type=2).values_list('pk','title')

# 利用extend扩展列表 (append只是添加)

before_download.extend(extra_list)

self.fields['before_download_select'].choices = before_download

after_download = [(0,'请选择')]

extra_list = models.HookTemplate.objects.filter(hook_type=4).values_list('pk', 'title')

after_download.extend(extra_list)

self.fields['after_download_select'].choices = after_download

before_deploy = [(0,'请选择')]

extra_list = models.HookTemplate.objects.filter(hook_type=6).values_list('pk', 'title')

before_deploy.extend(extra_list)

self.fields['before_deploy_select'].choices = before_deploy

实时获取数据动态展示

给前端所有的select标签绑定文本域变化事件(change事件)

// 直接给hooks类标签内所有的select绑定事件

$('.hooks').find('select').change(function () {

{#alert($(this).val()) 获取用户输入的模版主键值 #}

var $that = $(this);

// 朝后端发送请求 获取对应的脚本内容

$.ajax({

url:'/hook/template/'+$that.val()+'/',

type:'get',

dataType:'JSON',

success:function (args) {

// 获取脚本内容 渲染到对应下拉框下面的textarea框中

{#alert(args.content)#}

// 标签查找

$that.parent().parent().next().find('textarea').val(args.content);

}

})

})

def hook_template(request,hook_id):

# 根据hook_id查询出hook对象

hook_obj = models.HookTemplate.objects.filter(pk=hook_id).first()

back_dic = {'status':1000,'content':''}

back_dic['content'] = hook_obj.content

return JsonResponse(back_dic)

优化

用户一旦点击了checkbox按钮,那么就必须填写模版名称(进行校验)def 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','请输入模版名称')

注意,前端需要预留一部分内容展示错误信息否则会出现布局错乱的问题

{{ form_obj.after_deploy_template }}保存模版

{{ form_obj.after_deploy_title }}

{{ form_obj.after_deploy_title.errors.0 }}

发布任务

Ps:静态文件可以全局也可以在局部

静态文件的配置

# 1 配置文件中直接配置

STATICFILES_DIRS = [

os.path.join(BASE_DIR,'static1'),

os.path.join(BASE_DIR,'static2'),

]

# 2 模版语法直接配置

{% load staticfiles %}

新建发布任务接口

url(r'^deploy/(?P\d+)/$',deploy.deploy_task,name='deploy_task')

from django.shortcuts import HttpResponse,render,redirect,reverse

from app01 import models

def deploy_task(request,task_id):

task_obj = models.DeployTask.objects.filter(pk=task_id).first()

return render(request,'deploy.html',locals())

使用gojs展示流程图

// 由于ws和diagram需要在其他函数内使用 所以定义成全局变量

var ws;

var diagram;

function initWebSocket() {

ws = new WebSocket('ws://127.0.0.1:8000/publish/{{ task_obj.pk }}/');

// 一旦服务端有消息 会自动触发onmessage方法

ws.onmessage = function (args) {

// args.data

var res = JSON.parse(args.data);

if (res.code==='init'){

// 操作gojs渲染图表

diagram.model = new go.TreeModel(res.data)

}

}

}

function initTable() {

var $ = go.GraphObject.make;

diagram = $(go.Diagram, "diagramDiv", {

layout: $(go.TreeLayout, {

angle: 0,

nodeSpacing: 20,

layerSpacing: 70

})

});

// 生成一个节点模版

diagram.nodeTemplate = $(go.Node, "Auto",

$(go.Shape, {

figure: "RoundedRectangle",

fill: 'yellow',

stroke: 'yellow'

}, new go.Binding("figure", "figure"), new go.Binding("fill", "color"), new go.Binding("stroke", "color")),

$(go.TextBlock, {margin: 8}, new go.Binding("text", "text"))

);

// 生成一个箭头模版

diagram.linkTemplate = $(go.Link,

{routing: go.Link.Orthogonal},

$(go.Shape, {stroke: 'yellow'}, new go.Binding('stroke', 'link_color')),

$(go.Shape, {toArrow: "OpenTriangle", stroke: 'yellow'}, new go.Binding('stroke', 'link_color'))

);

// 数据集合 以后替换ajax请求 注意使用key和parent来规定箭头的指向

{#var nodeDataArray = [#}

{# {key: "start", text: '开始', figure: 'Ellipse', color: "lightgreen"},#}

{# {key: "download", parent: 'start', text: '下载代码', color: "lightgreen", link_text: '执行中...'},#}

{# {key: "compile", parent: 'download', text: '本地编译', color: "lightgreen"},#}

{# {key: "zip", parent: 'compile', text: '打包', color: "red", link_color: 'red'},#}

{# {key: "c1", text: '服务器1', parent: "zip"},#}

{# {key: "c11", text: '服务重启', parent: "c1"},#}

{# {key: "c2", text: '服务器2', parent: "zip"},#}

{# {key: "c21", text: '服务重启', parent: "c2"},#}

{# {key: "c3", text: '服务器3', parent: "zip"},#}

{# {key: "c31", text: '服务重启', parent: "c3"},#}

{#];#}

{#diagram.model = new go.TreeModel(nodeDataArray);#}

// 动态控制节点颜色变化 后端给一个key值 即可查找图表并修改

/*

var node = diagram.model.findNodeDataForKey("zip");

diagram.model.setDataProperty(node, "color", "lightgreen");

}

*/

}

// 页面加载完毕 先自动执行两个函数 给全局变量赋值

$(function () {

initWebSocket();

initTable()

});

function createDiagram() {

ws.send('init')

}

利用channels实现群发的功能

注册

INSTALLED_APPS = [

'django.contrib.admin',

...

'channels',

]

配置

ASGI_APPLICATION = 'dm_fabu03.routing.application'

新建routing

from channels.routing import ProtocolTypeRouter,URLRouter

from django.conf.urls import url

from app01 import consumers

"""consumers.py 当逻辑也非常多的时候 你也可以建成文件夹里面包含多个文件的形式"""

application = ProtocolTypeRouter({

'websocket':URLRouter([

url(r'^publish/(?P\d+)/$',consumers.PublishConsumer)

])

})

前端钩子脚本

task_form.html

{% extends 'base.html' %}

{% block css %}

.outline .series .module {

line-height: 100px;

vertical-align: middle;

width: 940px;

margin: 0 auto;

padding-bottom: 10px;

}

.outline .series .module .item .line {

float: left;

width: 80px;

}

.outline .series .module .item .line hr {

margin-top: 49px

}

.outline .series .module .item .icon {

float: left;

color: #dddddd;

position: relative;

}

.outline .series .module .item .icon .up, .outline .series .module .item .icon .down {

position: absolute;

line-height: 49px;

min-width: 90px;

left: 0;

text-align: center;

margin-left: -38px;

color: #337ab7;

}

.outline .series .module .item:hover .icon, .outline .series .module .item.active .icon {

color: green;

}

.outline .series .module .item .icon .up {

top: 0;

}

.outline .series .module .item .icon .down {

bottom: 0;

}

{% endblock %}

{% block content %}

{# 1 基本信息展示#}

项目名称:{{ project_obj.title }}环境:{{ project_obj.get_env_display }}
仓库地址:{{ project_obj.repo }}
线上地址:{{ project_obj.path }}
关联服务器

{% for server_obj in project_obj.servers.all %}

{{ server_obj.hostname }}

{% endfor %}

{% csrf_token %}

{# 2 基本配置#}

基本配置

class="col-sm-2 control-label">{{ form_obj.tag.label }}

{{ form_obj.tag }}

{{ form_obj.tag.errors.0 }}

{# 3 脚本钩子渲染#}

发布流程&脚本

{# 4 执行流程图即钩子脚本作用地展示#}

{# 5 四个脚本展示#}

02 下载前

{# 下拉框#}

{{ form_obj.before_download_select }}

{{ form_obj.before_download_script }}

{{ form_obj.before_download_template }}保存模版

{{ form_obj.before_download_title }}

{{ form_obj.before_download_title.errors.0 }}

04 下载后

{# 下拉框#}

{{ form_obj.after_download_select }}

{{ form_obj.after_download_script }}

{{ form_obj.after_download_template }}保存模版

{{ form_obj.after_download_title }}

{{ form_obj.after_download_title.errors.0 }}

06 发布前

{# 下拉框#}

{{ form_obj.before_deploy_select }}

{{ form_obj.before_deploy_script }}

{{ form_obj.before_deploy_template }}保存模版

{{ form_obj.before_deploy_title }}

{{ form_obj.before_deploy_title.errors.0 }}

08 发布后

{# 下拉框#}

{{ form_obj.after_deploy_select }}

{{ form_obj.after_deploy_script }}

{{ form_obj.after_deploy_template }}保存模版

{{ form_obj.after_deploy_title }}

{{ form_obj.after_deploy_title.errors.0 }}

{% endblock %}

{% block js %}

// 直接给hooks类标签内所有的select绑定事件

$('.hooks').find('select').change(function () {

{#alert($(this).val()) 获取用户输入的模版主键值 #}

var $that = $(this);

// 朝后端发送请求 获取对应的脚本内容

$.ajax({

url:'/hook/template/'+$that.val()+'/',

type:'get',

dataType:'JSON',

success:function (args) {

// 获取脚本内容 渲染到对应下拉框下面的textarea框中

{#alert(args.content)#}

// 标签查找

$that.parent().parent().next().find('textarea').val(args.content);

}

})

})

{% endblock %}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值