13 Oct 2016
使用django创建一个自动化运维系统,希望在页面上创建两个select标签,实现级联的效果, 即第二个select中的内容是由第一个select标签的选择所决定的。
可参照选择省份,然后在选择城市的那种级联效果
最终使用的解决方案是: (此处以brand和host为例,每个brand有多个host)
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class Brand(models.Model): brand = models.CharField(max_length=20) created = models.DateTimeField(auto_now_add=True, auto_now=False) updated = models.DateTimeField(auto_now_add=False, auto_now=True) def __str__(self): return self.brand def __unicode__(self): return self.brand class Host(models.Model): service_type_list = { ('web', "web"), ('ser', "service"), } host = models.GenericIPAddressField() hosttag = models.CharField(max_length=20) brand = models.ForeignKey(Brand) service_type = models.CharField(max_length=3, choices=service_type_list, default='web') created = models.DateTimeField(auto_now_add=True, auto_now=False) updated = models.DateTimeField(auto_now_add=False, auto_now=True) def __str__(self): return self.host def __unicode__(self): return self.host def save(self, *args, **kwargs): self.hosttag = self.host + '_' + self.brand.brand + '_' + self.service_type print self.hosttag super(Host, self).save(*args, **kwargs) # Host类中的brand设定为外键,对象是Brand类 # Host类重写了save方法,用来自动生成hosttag
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render, render_to_response from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login, logout from django.http import HttpResponseRedirect, HttpResponse from django.views.decorators.csrf import csrf_protect from subprocess import check_output from .models import Host, Brand import json # ...省略非相关函数及模块导入... def dashboard(request): if not request.user.is_authenticated(): return render(request, "devops/login.html") # username for main.html user_login_name = request.user.username # initial hosts and brands for selection form hosts = Host.objects.all() brands = ["请选择产品品牌"] for brand in Brand.objects.all(): if not brand in brands: brands.append(brand) context = { 'user_login_name': user_login_name, "hosts": hosts, "brands": brands, } # function for command running if request.POST: if(request.POST.get("run")): # strip去两边空格,split+join去除中间重复空格,然后split转换字符串为list cmd = ' '.join(request.POST.get('cmd').strip().split()).split() print cmd try: stdout = check_output(cmd) except: stdout = "CMD:" + str(cmd) + " CMD error:" + str(sys.exc_info()) context = { 'user_login_name': user_login_name, "hosts": hosts, "stdout": stdout, "brands": brands, } # runner = AnsibleRunner() # runner.init_inventory(host_list='localhost') # runner.init_play(hosts='localhost', module='shell', args='ls') # result = runner.run_it() return render(request, "devops/dashboard.html", context) @csrf_protect @login_required def form_interaction(request): if request.POST: selected_brand = request.POST.get('selbrand') host_list = [] filtered_host = Host.objects.filter(brand__brand=selected_brand) for brand_item in filtered_host: if host_list.count(brand_item) == 0: brand_item = str(brand_item) host_list.append(brand_item) host = json.dumps(host_list) return HttpResponse(host, content_type="application/json") ## dashboard函数用来响应dashboard页面的访问请求 # 其中处理了brand的<select>的初始化 ## from_interaction函数用来响应ajax的请求,用来返回第二个<select>的内容 # 需要注意的是,filtered_host = Host.objects.filter(brand__brand=selected_brand) # 一般的字段获取我们只要Host.objects.filter(host='something'),但是因为brand作为 # Host类的外键,所以需要brand__接Brand类中的brand字段名称,即"brand__brand"才可以 ## brand_item = str(brand_item) # 此举的意义是避免json.dumps()报错。
<form method="POST" action=""> {\% csrf_token \%} <div class="form-group"> <label class="control-label" for="selbrand">Brand</label> <select class="form-control" id="selbrand" name="selbrand"> {\% for brand in brands \%} <option>{{ brand }}</option> {\% endfor \%} </select> </div> <div class="form-group"> <label class="control-label" for="selhost">Host</label> <select class="form-control" id="selhost" name="selhost"> <option>select brand first</option> </select> <p id="test_p"></p> </div> <div class="form-group"> <label class="control-label" for="cmd">Command</label> <input type="text" class="form-control" id="cmd" placeholder="Enter cmd" name="cmd" value=''> </div> <button type="submit" class="btn btn-primary" name="run" value="Click">run</button> </form> <!--这部分重点注意id和class,这是html元素的身份标识-->
这部分代码也是在前端页面中
//jquery & ajax 部分 <script type="text/javascript"> $(document).ready(function(){ $.ajaxSetup({ beforeSend: function(xhr, settings) { function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) == (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { // Only send the token to relative URLs i.e. locally. xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); } } }); $("#selbrand").change(function(){ selbrand = $("#selbrand").val(); data = {selbrand: selbrand}; $.ajax({ url: "/form_interaction/", data: data, type: 'post', success: function(host){ $("#selhost").empty(); $("#selhost").append("<option>"+"请选择主机"+"</option>"); $.each(host,function(i){ $("#selhost").append("<option>"+host[i]+"</option>") }) } }) }); }); </script> // 此部分最为核心 // $(document).ready(function(){}含义是等待整个页面文档加载完成之后再去执行function // // $.ajaxSetup()含义是ajax的预配置,此段代码解决了csrf_token(django的中间件)认证的问题,否则ajax的post会报403 // // $("#selbrand").change(function(){$.ajax()}含义是当id为selbrand的部分发生了改变(第一个select发生改变), // 则执行()之内的函数。 // 首先,取得selbrand的值,组合成dict数据形式,用ajax POST到"/form_interaction/"。 // 然后,success: function(host){},含义为当post的结果是success(服务端未发生任何错误)时,执行{}内命令 // 其中的host为传入的参数,名称和views中的form_interaction函数return的值一致 // 首先ajax清空selhost(第二个select)的内容,然后再增加一条"<option>请选择主机</option>" // 最后,$.each(host,function(i){},含义为传入host,用each进行循环执行function(),i为每次拿出的变量, // 实际含义即把返回的host列表,每一项都以"<option>host[i]</option>"增加到<select>中
用来实现url访问时,映射到哪个views的函数的效果
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.conf.urls import url from . import views urlpatterns = [ ...省略其他url配置... url(r'^form_interaction/$', views.form_interaction, name='form_interaction'), ] # 当访问/from_interaction/时,把请求发送给views.py的form_interaction