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')
钩子脚本展示
针对四个钩子脚本,我们想要实现可以保存的功能
# 初始化字段
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=
# 初始化字段
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 执行流程图即钩子脚本作用地展示#}
01 开始
02 下载前
03 下载代码
04 下载后
05 打包上传
06 发布前
07 发布
08 发布后
{# 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 %}
mysql 钩子程序_20200319 代码发布之任务发布钩子脚本相关推荐
- 做一个检测钩子程序的工具
一.引言 Windows系统是建立在事件驱动的机制上的,每一个事件就是一个消息,每个运行中的程序,也就是所谓的进程,都维护者一个或多个消息队列,消息队列的个数取决于进程内包含的线程的个数.由于一个进程 ...
- Python【WINAPI】钩子程序获取账号密码等键盘输入信息
Python2.7.x版本 chr(lParam[0])会出现下面的类型转换错误,造成溢出的异常,在Python2.X版本的int是无限大,而C语言的长度有限制,所以就会出现这种溢出错误 hooked ...
- Hook 钩子技术及代码注入的 3 种方式
Hook 技术介绍 Hook 技术中文又叫作钩子技术,它就是在程序运行的过程中,对其中的某个方法进行重写, 在原有的方法前后加入我们自定义的代码.相当于在系统没有调用该函数之前,钩子程序就先捕获该消息 ...
- php新闻网页 毕设,【优质源码】校园新闻发布系统 php+mysql 毕设程序
校园新闻发布系统 php+mysql 毕设程序 (无图言diao???) (左边跟我一起画个龙,右边再画一个彩虹) 先上图,再BB 首页 登陆页 个人中心页 分类页 后台管理 后台管理 计算机专业毕业 ...
- Qt软件发布(版本信息,Release版程序,代码打包,制作安装包)
序言 当我们完成了Qt程序的开发,希望交予测试,或是正式发布的时候,需要将我们的程序进行层层封装,最终以一个安装包的形式呈现给用户.专业版的软件发布,以Qt软件为例,需要三个步骤:生成版本信息,生成R ...
- SpringBoot+Mysql+小程序实现的美团外卖美食小程序系统附带前台和后台代码整套源码包运行(问题解答)
博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 SpringBoot+Mysql+小程序实现的美团外卖美食小程序系 ...
- 通过VisualSVN的POST-COMMIT钩子自动部署代码
这段时间我们一直规划LSGO Group的学习网络平台,需求部分已经规划完毕,说做就做,开始搭建环境,由于利用PHP+MYSQL技术,在服务器端首先安装了WAMPServer,以便提供Apache服务 ...
- mysql 建立索引更慢_如何运用“提前发布,经常发布”来建立更好的品牌
mysql 建立索引更慢 开源的重要性,尤其是"早发布,经常发布"(RERO)的格言,很难被夸大. 随着各种规模和规模的组织发现开放的协作流程可以做什么,这种在命令行中诞生的方法已 ...
- 微信小程序实现运动步数排名与发布个人动态服务器部署
1. 项目规划 本项目为基于微信手机应用平台的一款运动互动型小程序,实现了用户即时运动步数群内PK与个人动态的发布,小程序前端采用原生框架,后端采用基于Node的koa2框架,数据库采用MYSQL,对 ...
最新文章
- 对delegate进行扩展 打造通用的计时完成方法
- 【云栖说第三期】发现大家对能模仿马云声音的ET有兴趣,我们找了阿里四位专家来聊聊ET背后的人工智能...
- MultiByteToWideChar和WideCharToMultiByte用法详解
- Titanic数据分析
- Windows中获取Redis指定前缀的Key并删除掉
- Serverless在大规模数据处理的实践
- JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式
- ASP.NET 2.0中将 GridView 导出到 Excel 文件中 (转)
- LinqToSQL下实现动态表名的映射
- 用 Java 实现人脸识别功能
- 计算机硬件系统设计原理 pdf,C1-1计算机硬件系统设计.pdf
- OceanBase 数据库
- php工业物联网实现,利用工业物联网网关巧妙实现远程控制
- OMEN惠普HP暗夜精灵5:win10下安装Ubuntu16.04双系统(win10+linux)
- Ubuntu下安装VS Code遇到的小问题
- 有钱人抢豪宅,普通人不敢消费:社会在割裂,富人更富,穷人更穷
- 计算机中负数和正数的二进制表示
- 安卓bmi项目_Android|BMI体质计算器实现(附测试源码)
- 发现苹果的MacOS支持图片OCR文字识别
- “拿下”家乐福中国,苏宁易购全场景零售进入新阶段