问题导读
1.如何在openstack中使用自定义插件?
2.如何通过ajax,把页面的数据传递给后台处理?
3.根据什么规则匹配到对应的views的JSONGetView类?
1、鉴于国内java开发人员比较多,java web方面的技术比较成熟。
所以用python django(openstack)框架和java的strurs做个类比,让大家能够更直观的理解、更快的进入到开发中:
附图大致对比下,具体细节,自行查找相关资料:
2、接下来正式开始讲解,如何在openstack中使用自定义插件,本文以Jquery zTree树形插件为例。
第一步封装url地址,请求数据,在urls中自定义url:
为了方便后续大家复制代码,也顺带附上源代码
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<tenant_id>[^/]+)/update/$',views.UpdateIsolationView.as_view(), name='update'),
url(r'^(?P<tenant_id>[^/]+)/tree/$',views.TreeView.as_view(), name='tree'),
url(r'^(?P<tenant_id>[^/]+)/get_tree_data/$',views.JSONGetView.as_view(), name='get_tree_data'),
url(r'^(?P<tenant_id>[^/]+)/set_tree$',views.JSONSetView.as_view(), name='set_tree'),
)
3、根据url规则匹配到对应的views的JSONGetView类:
- from django.views import generic
- from django.http import HttpResponse # noqa
- import json
- class JSONGetView(generic.View):
- def get(self, request, *args, **kwargs): #定义get方法
- tenant_id = self.kwargs["tenant_id"] #从url中截取参数值tenant_id
- aggregates = []
- try:
- aggregates = api.nova.aggregate_details_list(self.request)
- api.nova.isolatation_tree_list(self.request, tenant_id)
- except Exception:
- exceptions.handle(request,
- _('Unable to retrieve host aggregates list.'))
- aggregates.sort(key=lambda aggregate: aggregate.name.lower())
- #调用底层api 构造 zTree的数据
- tree_list = []
- for ag in aggregates:
- tree_data = {}
- tree_data['id'] = ag.availability_zone
- tree_data['name'] = ag.availability_zone
- tree_data['pId'] = 'NULL'
- tree_data['open'] = 'true'
- if tree_data not in tree_list:
- tree_list.append(tree_data)
- tree_data = {}
- tree_data['id'] = ag.name
- tree_data['name'] = ag.name
- tree_data['pId'] = ag.availability_zone
- tree_data['open'] = 'true'
- if tree_data not in tree_list:
- tree_list.append(tree_data)
- for hostname in ag.hosts:
- tree_data = {}
- tree_data['id'] = hostname
- tree_data['name'] = hostname
- tree_data['pId'] = ag.name
- tree_data['open'] = 'true'
- if tree_data not in tree_list:
- tree_list.append(tree_data)
- result = json.dumps(tree_list) #此处将python对象转换成json对象
- print (result)
- return HttpResponse(result,content_type="application/json")
复制代码
4、到此获取数据的url完成,测试下url(浏览器直接访问):
- http://192.168.10.31/dashboard/admin/isolations/2a4fe5e733e44982b1d576c5a0fe4bfd/get_tree_data/
- 得到数据:
- [{
- "open": "true",
- "pId": "NULL",
- "id": "zone1",
- "name": "zone1"
- },
- {
- "open": "true",
- "pId": "zone1",
- "id": "ag1",
- "name": "ag1"
- },
- {
- "open": "true",
- "pId": "ag1",
- "id": "node32",
- "name": "node32"
- },
- {
- "open": "true",
- "pId": "ag1",
- "id": "node31",
- "name": "node31"
- },
- {
- "open": "true",
- "pId": "zone1",
- "id": "ag2",
- "name": "ag2"
- },
- {
- "open": "true",
- "pId": "ag2",
- "id": "node33",
- "name": "node33"
- },
- {
- "open": "true",
- "pId": "NULL",
- "id": "zone2",
- "name": "zone2"
- },
- {
- "open": "true",
- "pId": "zone2",
- "id": "ag3",
- "name": "ag3"
- },
- {
- "open": "true",
- "pId": "ag3",
- "id": "node35",
- "name": "node35"
- },
- {
- "open": "true",
- "pId": "ag3",
- "id": "node34",
- "name": "node34"
- }]
复制代码
url能正确获取数据,测试通过。
5、用ajax在html页面中请求该url获取数据:
先附上页面模板代码:
其中javascript要写在标签{% block modal-body %}中才能失效,具体的zTree的js导入本文使用了为压缩的方式,直接导入。
- {# add by ttx 2014-9-25#}
- <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/js/zTree/css/demo.css" type="text/css">
- <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/js/zTree/css/zTreeStyle/zTreeStyle.css" type="text/css">
- <script type="text/javascript" src="{{ STATIC_URL }}dashboard/js/zTree/js/jquery.ztree.core-3.5.js"></script>
- <script type="text/javascript" src="{{ STATIC_URL }}dashboard/js/zTree/js/jquery.ztree.excheck-3.5.js"></script>
复制代码
zTree插件路径:
详解ajax请求:
- var url = '/dashboard/admin/isolations/{{ tenant_id }}/get_tree_data'; #此处为ajax的url地址,与步骤4中浏览器访问一致
- $.ajax({ #其中$代表Jquery插件,ajax为Jquery插件的方法
- type: "get", #type主要有get、post。其中get用于获取数据,是幂等操作、post用于跟新数据
- async: false, #false代码同步刷新,true代表异步刷新。本示例需要等到数据返回再进行tree的渲染,因此需要同步
- url: url, #请求的url地址
- dataType: "json", #数据返回类型为json
- success: function (data) { #ajax请求成功之后得到数据data,执行success:后面function里面代码
- json = JSON.stringify(data);
- host_tree_data = JSON.parse(json)
- }
- });
复制代码
更多ajax详细使用讲解请自行参考相关资料。
后续就是具体的zTree根据得到的数据host_tree_data ,生成zone、aggregate、host的节点树,附上简单代码,不详解:
- {% extends "horizon/common/_modal_form.html" %}
- {% load i18n %}
- {% load url from future %}
- {% block form_id %}create_image_form{% endblock %}
- {% block form_action %}{% url 'horizon:admin:images:create' %}{% endblock %}
- {% block form_attrs %}enctype="multipart/form-data"{% endblock %}
- {% block modal-header %}{% trans "Isolatation tree" %}{% endblock %}
- {% block modal-body %}
- <SCRIPT type="text/javascript">
- <!--
- var setting = {
- view: {
- selectedMulti: false
- },
- check: {
- enable: true
- },
- data: {
- simpleData: {
- enable: true
- }
- },
- callback: {
- beforeCheck: beforeCheck,
- onCheck: onCheck
- }
- };
- var code, log, className = "dark";
- function beforeCheck(treeId, treeNode) {
- className = (className === "dark" ? "":"dark");
- showLog("[ "+getTime()+" beforeCheck ] " + treeNode.name );
- return (treeNode.doCheck !== false);
- }
- function onCheck(e, treeId, treeNode) {
- showLog("[ "+getTime()+" onCheck ] " + treeNode.name );
- getAllChangeNodes()
- }
- function getAllChangeNodes() {
- var treeObj = $.fn.zTree.getZTreeObj("treeDemo");
- var nodes = treeObj.getChangeCheckedNodes();
- $('#mytest').html(JSON.stringify(nodes));
- var url = '/dashboard/admin/isolations/{{ tenant_id }}/set_tree';
- var data={};
- data["jsonTree"] = JSON.stringify(nodes);
- jQuery.ajax({
- type:"POST",
- url : url,
- data:data,
- dataType : "json",
- beforeSend: function(xhr, settings){
- var csrftoken = $.cookie('csrftoken');
- xhr.setRequestHeader("X-CSRFToken", csrftoken);
- },
- success : function(data) {
- }
- });
- }
- function showLog(str) {
- if (!log) log = $("#log");
- log.append("<li class='"+className+"'>"+str+"</li>");
- if(log.children("li").length > 6) {
- log.get(0).removeChild(log.children("li")[0]);
- }
- }
- function getTime() {
- var now= new Date(),
- h=now.getHours(),
- m=now.getMinutes(),
- s=now.getSeconds(),
- ms=now.getMilliseconds();
- return (h+":"+m+":"+s+ " " +ms);
- }
- function checkNode(e) {
- var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
- type = e.data.type,
- nodes = zTree.getSelectedNodes();
- if (type.indexOf("All")<0 && nodes.length == 0) {
- alert("请先选择一个节点");
- }
- if (type == "checkAllTrue") {
- zTree.checkAllNodes(true);
- } else if (type == "checkAllFalse") {
- zTree.checkAllNodes(false);
- } else {
- var callbackFlag = $("#callbackTrigger").attr("checked");
- for (var i=0, l=nodes.length; i<l; i++) {
- if (type == "checkTrue") {
- zTree.checkNode(nodes[i], true, false, callbackFlag);
- } else if (type == "checkFalse") {
- zTree.checkNode(nodes[i], false, false, callbackFlag);
- } else if (type == "toggle") {
- zTree.checkNode(nodes[i], null, false, callbackFlag);
- }else if (type == "checkTruePS") {
- zTree.checkNode(nodes[i], true, true, callbackFlag);
- } else if (type == "checkFalsePS") {
- zTree.checkNode(nodes[i], false, true, callbackFlag);
- } else if (type == "togglePS") {
- zTree.checkNode(nodes[i], null, true, callbackFlag);
- }
- }
- }
- }
- function setAutoTrigger(e) {
- var zTree = $.fn.zTree.getZTreeObj("treeDemo");
- zTree.setting.check.autoCheckTrigger = $("#autoCallbackTrigger").attr("checked");
- $("#autoCheckTriggerValue").html(zTree.setting.check.autoCheckTrigger ? "true" : "false");
- }
- $(document).ready(function(){
- var url = '/dashboard/admin/isolations/{{ tenant_id }}/get_tree_data';
- $.ajax({
- type: "get",
- async: false,
- url: url,
- dataType: "json",
- success: function (data) {
- json = JSON.stringify(data);
- host_tree_data = JSON.parse(json)
- }
- });
- var zNodes =[
- { id:1, pId:0, name:"1", open:true},
- { id:11, pId:1, name:"1-1"},
- { id:12, pId:1, name:"1-2"},
- { id:111, pId:11, name:"1-1-1","checked":"true"},
- { id:112, pId:11, name:"1-1-2"},
- ];
- $.fn.zTree.init($("#treeDemo"), setting, host_tree_data);
- $("#checkTrue").bind("click", {type:"checkTrue"}, checkNode);
- $("#checkFalse").bind("click", {type:"checkFalse"}, checkNode);
- $("#toggle").bind("click", {type:"toggle"}, checkNode);
- $("#checkTruePS").bind("click", {type:"checkTruePS"}, checkNode);
- $("#checkFalsePS").bind("click", {type:"checkFalsePS"}, checkNode);
- $("#togglePS").bind("click", {type:"togglePS"}, checkNode);
- $("#checkAllTrue").bind("click", {type:"checkAllTrue"}, checkNode);
- $("#checkAllFalse").bind("click", {type:"checkAllFalse"}, checkNode);
- $("#autoCallbackTrigger").bind("change", {}, setAutoTrigger);
- });
- //-->
- </SCRIPT>
- <h1>用 zTree 方法 勾选 checkbox</h1>
- <h6>[ 文件路径: excheck/checkbox_fun.html ]</h6>
- <div class="content_wrap">
- <div class="zTreeDemoBackground left">
- <ul id="treeDemo" class="ztree"></ul>
- </div>
- </div>
- <div>
- <ul class="info">
- <div id="mytest">11111111111111111111111111111111111111111111</div>
- <li class="title"><h2>1、beforeCheck / onCheck 事件回调函数控制</h2>
- <ul class="list">
- <li>利用 beforeCheck / onCheck 事件回调函数 可以控制是否允许 更改 节点勾选状态,这里简单演示如何监控此事件</li>
- <li><p>这里还演示了 checkNode / checkAllNodes 方法触发 beforeCheck / onCheck 事件回调函数的情况,试试看:<br/>
- <input type="checkbox" id="autoCallbackTrigger" /> setting.check.autoCheckTrigger: <span id="autoCheckTriggerValue">false</span><br/>
- <input type="checkbox" id="callbackTrigger" checked /> 执行勾选方法是否触发 callback <br/>
- 单节点--[ <a id="checkTrue" href="#" title="不想勾选我就不勾选你..." onclick="return false;">勾选</a> ]
- [ <a id="checkFalse" href="#" title="不想取消勾选我就不取消你..." onclick="return false;">取消勾选</a> ]
- [ <a id="toggle" href="#" title="你想怎样?..." onclick="return false;">勾选 切换</a> ]<br/>
- 单节点 ( 影响父子 )--[ <a id="checkTruePS" href="#" title="不想勾选我就不勾选你..." onclick="return false;">勾选</a> ]
- [ <a id="checkFalsePS" href="#" title="不想取消勾选我就不取消你..." onclick="return false;">取消勾选</a> ]
- [ <a id="togglePS" href="#" title="你想怎样?..." onclick="return false;">勾选 切换</a> ]<br/>
- 全部节点--[ <a id="checkAllTrue" href="#" title="不管你有多NB,统统都要听我的!!" onclick="return false;">勾选</a> ]
- [ <a id="checkAllFalse" href="#" title="不管你有多NB,统统都要听我的!!" onclick="return false;">取消勾选</a> ]</p>
- </li>
- <li><p><span class="highlight_red">使用 zTreeObj.checkNode / checkAllNodes 方法,详细请参见 API 文档中的相关内容</span><br/>
- beforeCheck / onCheck log:<br/>
- <ul id="log" class="log" style="height:130px;"></ul></p>
- </li>
- </ul>
- </li>
- <li class="title"><h2>2、setting 配置信息说明</h2>
- <ul class="list">
- <li>同 "checkbox 勾选操作" 中的说明</li>
- </ul>
- </li>
- <li class="title"><h2>3、treeNode 节点数据说明</h2>
- <ul class="list">
- <li>同 "checkbox 勾选操作" 中的说明</li>
- </ul>
- </li>
- </ul>
- </div>
- {% endblock %}
- {% block modal-footer %}
- <input class="btn btn-primary pull-right" type="submit" value="{% trans "Save" %}" />
- <a href="{% url 'horizon:admin:images:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
- {% endblock %}
复制代码
跟多zTree使用,请自行参考zTree官网:www.ztree.me/
页面最终展示:
http://www.js-css.cn/jscode/nav/nav23/
到此通过ajax获取数据到页面展示讲解完毕。
6、讲完ajax从后台获取数据到前台接下来讲解,如何通过ajax,把页面的数据传递给后台处理:
附上代码:
function getAllChangeNodes() {
var treeObj = $.fn.zTree.getZTreeObj("treeDemo");
var nodes = treeObj.getChangeCheckedNodes();
$('#mytest').html(JSON.stringify(nodes)); #JSON.stringify(nodes)将js对象nodes,转化为json对象
var url = '/dashboard/admin/isolations/{{ tenant_id }}/set_tree';
var data={}; #url中传递的数据,相当于$url?jsonTree=JSON.stringify(nodes)
data["jsonTree"] = JSON.stringify(nodes);
jQuery.ajax({
type:"POST", #ajax类型,post进来进行更新
url : url,
data:data,
dataType : "json",
beforeSend: function(xhr, settings){ #此处的beforeSend用来解决ajax在django中报csrftoken错误
var csrftoken = $.cookie('csrftoken');
xhr.setRequestHeader("X-CSRFToken", csrftoken);
},
success : function(data) { #ajax请求成功之后执行
}
});
}
根据上述url = '/dashboard/admin/isolations/{{ tenant_id }}/set_tree';到urls.py中找对应匹配的url:
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<tenant_id>[^/]+)/update/$',views.UpdateIsolationView.as_view(), name='update'),
url(r'^(?P<tenant_id>[^/]+)/tree/$',views.TreeView.as_view(), name='tree'),
url(r'^(?P<tenant_id>[^/]+)/get_tree_data/$',views.JSONGetView.as_view(), name='get_tree_data'),
url(r'^(?P<tenant_id>[^/]+)/set_tree$',views.JSONSetView.as_view(), name='set_tree'),
)
根据url匹配规则,跟进到JSONSetView:
- class JSONSetView(generic.View):
- def post(self, request, *args, **kwargs): #因为ajax的请求类型为post因此实现post函数,否则会报错
- tenant_id = self.kwargs["tenant_id"] #从URL中获取tenant_id
- json_tree = request.POST.get("jsonTree") #从ajax发过来的请求中获取jsonTree json数据
- tree_change_nodes = json.loads(json_tree) #将页面传递的json数据,转换为python对象,具体转换规则自行参考
- #此处根据前端传送的数据,调用api传递给后端处理
- api.nova.isolatation_add_tree(request, tenant_id, tree_change_nodes)
- return HttpResponse(tree_change_nodes,content_type="application/json")
复制代码
|