-
代碼發布整體工作流程


代碼發布這個功能可以基於很多方式很多語言來實現
我們這里主要用的是python相關的知識點來完成的,大體邏輯流程都是大差不差的
額外補充:p2p(比特流技術),為了減輕服務器的壓力,將所有人即是下載者又是資源的上傳者
-
服務器表的增刪改查
我們從頭到位將增刪改查自己實現了一遍,目的是為了搭建項目增刪改查基本業務邏輯,方便后續其他表的操作
modelform使用
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中相同的代碼抽取出來形成基類
給項目表再增兩個字段
線上項目地址、關聯服務器
-
發布任務
由於發布任務是針對項目的,所以為了之后新增任務的時候不需要自己選擇項目,所以我們將發布任務做成項目表中的一個字段,點擊該字段跳轉到該項目對應的所有發布紀錄中,之后在該發布紀錄頁面上開設新增按鈕,將當前項目的主鍵值傳遞到后端,這樣的話新增任務就無需自己選擇項目了
內容概要
-
發布任務單新增頁面
-
發布流程前端實時展示
任務列表的展示為了更加人性化,可以增加當前項目的項目名和環境
# 先簡單的手動處理數據 完成添加功能
from app01.myforms.base import BaseModelForm
from app01 import models
class TaskModelForm(BaseModelForm):
class Meta:
model = models.DeployTask
fields = '__all__'
# 項目唯一標識 應該是自動生成的無需用戶填寫
# 項目由於已經帶了主鍵值了 所以也無需用戶填寫
# 創建的任務單默認狀態就是待發布 所以也無需展示
exclude = ['uid','project','status']
def __init__(self,project_id,*args,**kwargs):
super().__init__(*args,**kwargs)
self.project_id = project_id
def save(self, commit=True):
# 添加數據
self.instance.uid = 'sadjasdj'
self.instance.project_id = self.project_id
# 調用父類的save方法保存數據
super().save(commit=True)
接下來,我們針對任務的添加頁面,單獨開設一個html
並在該html頁面上划分三塊區域展示不同的信息
-
當前任務關聯的項目基本信息展示
-
基本配置
-
腳本書寫
針對四個鈎子腳本,我們想要實現可以保存的功能
# 初始化字段
def __init__(self,project_obj,*args,**kwargs):
super().__init__(*args,**kwargs)
self.project_obj = project_obj
# 初始化選擇框內容
self.init_hook()
def init_hook(self):
# 給所有的下拉框先添加一個 請選擇選項
# <option value="0">請選擇</option> (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方法中 做判斷!!!
# 判斷用戶是否點擊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
)
數據庫模版名稱前端展示,以及用戶點擊實時獲取模版內容
def init_hook(self):
# 給所有的下拉框先添加一個 請選擇選項
# <option value="0">請選擇</option> (0,'請選擇')
before_download = [(0,'請選擇')]
# 還應該去數據庫中查詢是否有對應的模版名稱
extra_list = models.HookTemplate.objects.filter(hook_type=2).values_list('pk','title')
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
after_deploy = [(0,'請選擇')]
extra_list = models.HookTemplate.objects.filter(hook_type=8).values_list('pk', 'title')
after_deploy.extend(extra_list)
self.fields['after_deploy_select'].choices = after_deploy
前端用戶點擊模版動態展示內容
給前端所有的select標簽綁定文本域變化事件(change事件)
<script>
// 直接給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);
}
})
})
</script>
優化
-
用戶一旦點擊了checkbox按鈕,那么就必須填寫模版名稱
# 鈎子函數 全局鈎子 局部鈎子 # 全局鈎子 # 全局鈎子校驗用戶是否點擊checkbox 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-sm-3"> <div class="checkbox"> <label for="">{{ form_obj.after_deploy_template }}保存模版</label> </div> </div> <div class="col-sm-9"> {{ form_obj.after_deploy_title }} <span style="color: red">{{ form_obj.after_deploy_title.errors.0 }}</span> </div> </div>發布任務
Ps:靜態文件可以全局也可以在局部
靜態文件配置
# 1 配置文件中直接配置 STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static1'), os.path.join(BASE_DIR,'static2'), ] # 2 模版語法直接配置 {% load staticfiles %} <script src="{% static 'js/go.js' %}"></script><script> // 由於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') } </script>
