分享

openstack页面自定义插件使用详解(django、ajax、post)(zTree为例)

问题导读
1.如何在openstack中使用自定义插件?
2.如何通过ajax,把页面的数据传递给后台处理?
3.根据什么规则匹配到对应的views的JSONGetView类?






1、鉴于国内java开发人员比较多,java web方面的技术比较成熟。
所以用python django(openstack)框架和java的strurs做个类比,让大家能够更直观的理解、更快的进入到开发中:

附图大致对比下,具体细节,自行查找相关资料:

1.png




2、接下来正式开始讲解,如何在openstack中使用自定义插件,本文以Jquery zTree树形插件为例。

第一步封装url地址,请求数据,在urls中自定义url:

2.png


为了方便后续大家复制代码,也顺带附上源代码
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类:


3.png




  1. from django.views import generic
  2. from django.http import HttpResponse # noqa
  3. import json
  4. class JSONGetView(generic.View):
  5.     def get(self, request, *args, **kwargs): #定义get方法
  6.         tenant_id = self.kwargs["tenant_id"] #从url中截取参数值tenant_id
  7.         aggregates = []
  8.         try:
  9.             aggregates = api.nova.aggregate_details_list(self.request)
  10.             api.nova.isolatation_tree_list(self.request, tenant_id)
  11.         except Exception:
  12.             exceptions.handle(request,
  13.                               _('Unable to retrieve host aggregates list.'))
  14.         aggregates.sort(key=lambda aggregate: aggregate.name.lower())
  15. #调用底层api 构造 zTree的数据
  16.         tree_list = []
  17.         for ag in aggregates:
  18.             tree_data = {}
  19.             tree_data['id'] = ag.availability_zone
  20.             tree_data['name'] = ag.availability_zone
  21.             tree_data['pId'] = 'NULL'
  22.             tree_data['open'] = 'true'
  23.             if tree_data not in tree_list:
  24.                 tree_list.append(tree_data)
  25.             tree_data = {}
  26.             tree_data['id'] = ag.name
  27.             tree_data['name'] = ag.name
  28.             tree_data['pId'] = ag.availability_zone
  29.             tree_data['open'] = 'true'
  30.             if tree_data not in tree_list:
  31.                 tree_list.append(tree_data)
  32.             for hostname in ag.hosts:
  33.                 tree_data = {}
  34.                 tree_data['id'] = hostname
  35.                 tree_data['name'] = hostname
  36.                 tree_data['pId'] = ag.name
  37.                 tree_data['open'] = 'true'
  38.                 if tree_data not in tree_list:
  39.                     tree_list.append(tree_data)
  40.         result = json.dumps(tree_list) #此处将python对象转换成json对象
  41.         print (result)
  42.         return HttpResponse(result,content_type="application/json")
复制代码







4、到此获取数据的url完成,测试下url(浏览器直接访问):


4.png


  1. http://192.168.10.31/dashboard/admin/isolations/2a4fe5e733e44982b1d576c5a0fe4bfd/get_tree_data/
  2. 得到数据:
  3. [{
  4. "open": "true",
  5. "pId": "NULL",
  6. "id": "zone1",
  7. "name": "zone1"
  8. },
  9. {
  10. "open": "true",
  11. "pId": "zone1",
  12. "id": "ag1",
  13. "name": "ag1"
  14. },
  15. {
  16. "open": "true",
  17. "pId": "ag1",
  18. "id": "node32",
  19. "name": "node32"
  20. },
  21. {
  22. "open": "true",
  23. "pId": "ag1",
  24. "id": "node31",
  25. "name": "node31"
  26. },
  27. {
  28. "open": "true",
  29. "pId": "zone1",
  30. "id": "ag2",
  31. "name": "ag2"
  32. },
  33. {
  34. "open": "true",
  35. "pId": "ag2",
  36. "id": "node33",
  37. "name": "node33"
  38. },
  39. {
  40. "open": "true",
  41. "pId": "NULL",
  42. "id": "zone2",
  43. "name": "zone2"
  44. },
  45. {
  46. "open": "true",
  47. "pId": "zone2",
  48. "id": "ag3",
  49. "name": "ag3"
  50. },
  51. {
  52. "open": "true",
  53. "pId": "ag3",
  54. "id": "node35",
  55. "name": "node35"
  56. },
  57. {
  58. "open": "true",
  59. "pId": "ag3",
  60. "id": "node34",
  61. "name": "node34"
  62. }]
复制代码



url能正确获取数据,测试通过。


5、用ajax在html页面中请求该url获取数据:

先附上页面模板代码:


5.png


6.png



其中javascript要写在标签{% block modal-body %}中才能失效,具体的zTree的js导入本文使用了为压缩的方式,直接导入。

7.png



  1. {# add by ttx 2014-9-25#}
  2.   <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/js/zTree/css/demo.css" type="text/css">
  3. <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/js/zTree/css/zTreeStyle/zTreeStyle.css" type="text/css">
  4. <script type="text/javascript" src="{{ STATIC_URL }}dashboard/js/zTree/js/jquery.ztree.core-3.5.js"></script>
  5. <script type="text/javascript" src="{{ STATIC_URL }}dashboard/js/zTree/js/jquery.ztree.excheck-3.5.js"></script>
复制代码




zTree插件路径:

8.png



详解ajax请求:
  1. var url = '/dashboard/admin/isolations/{{ tenant_id }}/get_tree_data'; #此处为ajax的url地址,与步骤4中浏览器访问一致
  2. $.ajax({ #其中$代表Jquery插件,ajax为Jquery插件的方法
  3.              type: "get", #type主要有get、post。其中get用于获取数据,是幂等操作、post用于跟新数据
  4.              async: false, #false代码同步刷新,true代表异步刷新。本示例需要等到数据返回再进行tree的渲染,因此需要同步
  5.              url: url, #请求的url地址
  6.              dataType: "json", #数据返回类型为json
  7.               success: function (data) { #ajax请求成功之后得到数据data,执行success:后面function里面代码
  8.                            json = JSON.stringify(data);
  9.                            host_tree_data = JSON.parse(json)
  10.                 }
  11. });
复制代码

更多ajax详细使用讲解请自行参考相关资料。


后续就是具体的zTree根据得到的数据host_tree_data ,生成zone、aggregate、host的节点树,附上简单代码,不详解:
  1. {% extends "horizon/common/_modal_form.html" %}
  2. {% load i18n %}
  3. {% load url from future %}
  4. {% block form_id %}create_image_form{% endblock %}
  5. {% block form_action %}{% url 'horizon:admin:images:create' %}{% endblock %}
  6. {% block form_attrs %}enctype="multipart/form-data"{% endblock %}
  7. {% block modal-header %}{% trans "Isolatation tree" %}{% endblock %}
  8. {% block modal-body %}
  9. <SCRIPT type="text/javascript">
  10.   <!--
  11.   var setting = {
  12.    view: {
  13.     selectedMulti: false
  14.    },
  15.    check: {
  16.     enable: true
  17.    },
  18.    data: {
  19.     simpleData: {
  20.      enable: true
  21.     }
  22.    },
  23.    callback: {
  24.     beforeCheck: beforeCheck,
  25.     onCheck: onCheck
  26.    }
  27.   };
  28.   var code, log, className = "dark";
  29.   function beforeCheck(treeId, treeNode) {
  30.    className = (className === "dark" ? "":"dark");
  31.    showLog("[ "+getTime()+" beforeCheck ]    " + treeNode.name );
  32.    return (treeNode.doCheck !== false);
  33.   }
  34.   function onCheck(e, treeId, treeNode) {
  35.    showLog("[ "+getTime()+" onCheck ]    " + treeNode.name );
  36.             getAllChangeNodes()
  37.   }
  38.         function getAllChangeNodes() {
  39.    var treeObj = $.fn.zTree.getZTreeObj("treeDemo");
  40.             var nodes = treeObj.getChangeCheckedNodes();
  41.             $('#mytest').html(JSON.stringify(nodes));
  42.             var url = '/dashboard/admin/isolations/{{ tenant_id }}/set_tree';
  43.             var data={};
  44.             data["jsonTree"] = JSON.stringify(nodes);
  45.             jQuery.ajax({
  46.                 type:"POST",
  47.                 url : url,
  48.                 data:data,
  49.                 dataType : "json",
  50.                 beforeSend: function(xhr, settings){
  51.                       var csrftoken = $.cookie('csrftoken');
  52.                       xhr.setRequestHeader("X-CSRFToken", csrftoken);
  53.                   },
  54.                 success : function(data) {
  55.                 }
  56.             });
  57.   }
  58.   function showLog(str) {
  59.    if (!log) log = $("#log");
  60.    log.append("<li class='"+className+"'>"+str+"</li>");
  61.    if(log.children("li").length > 6) {
  62.     log.get(0).removeChild(log.children("li")[0]);
  63.    }
  64.   }
  65.   function getTime() {
  66.    var now= new Date(),
  67.    h=now.getHours(),
  68.    m=now.getMinutes(),
  69.    s=now.getSeconds(),
  70.    ms=now.getMilliseconds();
  71.    return (h+":"+m+":"+s+ " " +ms);
  72.   }
  73.   function checkNode(e) {
  74.    var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
  75.    type = e.data.type,
  76.    nodes = zTree.getSelectedNodes();
  77.    if (type.indexOf("All")<0 && nodes.length == 0) {
  78.     alert("请先选择一个节点");
  79.    }
  80.    if (type == "checkAllTrue") {
  81.     zTree.checkAllNodes(true);
  82.    } else if (type == "checkAllFalse") {
  83.     zTree.checkAllNodes(false);
  84.    } else {
  85.     var callbackFlag = $("#callbackTrigger").attr("checked");
  86.     for (var i=0, l=nodes.length; i<l; i++) {
  87.      if (type == "checkTrue") {
  88.       zTree.checkNode(nodes[i], true, false, callbackFlag);
  89.      } else if (type == "checkFalse") {
  90.       zTree.checkNode(nodes[i], false, false, callbackFlag);
  91.      } else if (type == "toggle") {
  92.       zTree.checkNode(nodes[i], null, false, callbackFlag);
  93.      }else if (type == "checkTruePS") {
  94.       zTree.checkNode(nodes[i], true, true, callbackFlag);
  95.      } else if (type == "checkFalsePS") {
  96.       zTree.checkNode(nodes[i], false, true, callbackFlag);
  97.      } else if (type == "togglePS") {
  98.       zTree.checkNode(nodes[i], null, true, callbackFlag);
  99.      }
  100.     }
  101.    }
  102.   }
  103.   function setAutoTrigger(e) {
  104.    var zTree = $.fn.zTree.getZTreeObj("treeDemo");
  105.    zTree.setting.check.autoCheckTrigger = $("#autoCallbackTrigger").attr("checked");
  106.    $("#autoCheckTriggerValue").html(zTree.setting.check.autoCheckTrigger ? "true" : "false");
  107.   }
  108.   $(document).ready(function(){
  109.             var url = '/dashboard/admin/isolations/{{ tenant_id }}/get_tree_data';
  110.             $.ajax({
  111.                        type: "get",
  112.                        async: false,
  113.                        url: url,
  114.                        dataType: "json",
  115.                        success: function (data) {
  116.                            json = JSON.stringify(data);
  117.                            host_tree_data = JSON.parse(json)
  118.                        }
  119.                    });
  120.             var zNodes =[
  121.                             { id:1, pId:0, name:"1", open:true},
  122.                             { id:11, pId:1, name:"1-1"},
  123.                             { id:12, pId:1, name:"1-2"},
  124.                             { id:111, pId:11, name:"1-1-1","checked":"true"},
  125.                             { id:112, pId:11, name:"1-1-2"},
  126.                         ];
  127.    $.fn.zTree.init($("#treeDemo"), setting, host_tree_data);
  128.    $("#checkTrue").bind("click", {type:"checkTrue"}, checkNode);
  129.    $("#checkFalse").bind("click", {type:"checkFalse"}, checkNode);
  130.    $("#toggle").bind("click", {type:"toggle"}, checkNode);
  131.    $("#checkTruePS").bind("click", {type:"checkTruePS"}, checkNode);
  132.    $("#checkFalsePS").bind("click", {type:"checkFalsePS"}, checkNode);
  133.    $("#togglePS").bind("click", {type:"togglePS"}, checkNode);
  134.    $("#checkAllTrue").bind("click", {type:"checkAllTrue"}, checkNode);
  135.    $("#checkAllFalse").bind("click", {type:"checkAllFalse"}, checkNode);
  136.    $("#autoCallbackTrigger").bind("change", {}, setAutoTrigger);
  137.   });
  138.   //-->
  139. </SCRIPT>
  140. <h1>用 zTree 方法 勾选 checkbox</h1>
  141. <h6>[ 文件路径: excheck/checkbox_fun.html ]</h6>
  142. <div class="content_wrap">
  143. <div class="zTreeDemoBackground left">
  144.   <ul id="treeDemo" class="ztree"></ul>
  145. </div>
  146. </div>
  147. <div>
  148.   <ul class="info">
  149.             <div id="mytest">11111111111111111111111111111111111111111111</div>
  150.    <li class="title"><h2>1、beforeCheck / onCheck 事件回调函数控制</h2>
  151.     <ul class="list">
  152.     <li>利用 beforeCheck / onCheck 事件回调函数 可以控制是否允许 更改 节点勾选状态,这里简单演示如何监控此事件</li>
  153.     <li><p>这里还演示了 checkNode / checkAllNodes 方法触发 beforeCheck / onCheck 事件回调函数的情况,试试看:<br/>
  154.          <input type="checkbox" id="autoCallbackTrigger" /> setting.check.autoCheckTrigger: <span id="autoCheckTriggerValue">false</span><br/>
  155.          <input type="checkbox" id="callbackTrigger" checked /> 执行勾选方法是否触发 callback <br/>
  156.          单节点--[ <a id="checkTrue" href="#" title="不想勾选我就不勾选你..." onclick="return false;">勾选</a> ]
  157.          [ <a id="checkFalse" href="#" title="不想取消勾选我就不取消你..." onclick="return false;">取消勾选</a> ]
  158.          [ <a id="toggle" href="#" title="你想怎样?..." onclick="return false;">勾选 切换</a> ]<br/>
  159.          单节点 ( 影响父子 )--[ <a id="checkTruePS" href="#" title="不想勾选我就不勾选你..." onclick="return false;">勾选</a> ]
  160.          [ <a id="checkFalsePS" href="#" title="不想取消勾选我就不取消你..." onclick="return false;">取消勾选</a> ]
  161.          [ <a id="togglePS" href="#" title="你想怎样?..." onclick="return false;">勾选 切换</a> ]<br/>
  162.          全部节点--[ <a id="checkAllTrue" href="#" title="不管你有多NB,统统都要听我的!!" onclick="return false;">勾选</a> ]
  163.          [ <a id="checkAllFalse" href="#" title="不管你有多NB,统统都要听我的!!" onclick="return false;">取消勾选</a> ]</p>
  164.     </li>
  165.     <li><p><span class="highlight_red">使用 zTreeObj.checkNode / checkAllNodes 方法,详细请参见 API 文档中的相关内容</span><br/>
  166.      beforeCheck / onCheck log:<br/>
  167.      <ul id="log" class="log" style="height:130px;"></ul></p>
  168.     </li>
  169.     </ul>
  170.    </li>
  171.    <li class="title"><h2>2、setting 配置信息说明</h2>
  172.     <ul class="list">
  173.     <li>同 "checkbox 勾选操作" 中的说明</li>
  174.     </ul>
  175.    </li>
  176.    <li class="title"><h2>3、treeNode 节点数据说明</h2>
  177.     <ul class="list">
  178.     <li>同 "checkbox 勾选操作" 中的说明</li>
  179.     </ul>
  180.    </li>
  181.   </ul>
  182. </div>
  183. {% endblock %}
  184. {% block modal-footer %}
  185.   <input class="btn btn-primary pull-right" type="submit" value="{% trans "Save" %}" />
  186.   <a href="{% url 'horizon:admin:images:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
  187. {% endblock %}
复制代码





跟多zTree使用,请自行参考zTree官网:www.ztree.me/

页面最终展示:
9.png

http://www.js-css.cn/jscode/nav/nav23/
10.png


到此通过ajax获取数据到页面展示讲解完毕。

6、讲完ajax从后台获取数据到前台接下来讲解,如何通过ajax,把页面的数据传递给后台处理:




11.png



附上代码:

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:


12.png



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:

13.png




  1. class JSONSetView(generic.View):
  2.     def post(self, request, *args, **kwargs): #因为ajax的请求类型为post因此实现post函数,否则会报错
  3.         tenant_id = self.kwargs["tenant_id"] #从URL中获取tenant_id
  4.         json_tree = request.POST.get("jsonTree") #从ajax发过来的请求中获取jsonTree json数据
  5.         tree_change_nodes = json.loads(json_tree) #将页面传递的json数据,转换为python对象,具体转换规则自行参考
  6. #此处根据前端传送的数据,调用api传递给后端处理
  7.         api.nova.isolatation_add_tree(request, tenant_id, tree_change_nodes)
  8.         return HttpResponse(tree_change_nodes,content_type="application/json")
复制代码









已有(1)人评论

跳转到指定楼层
jugort 发表于 2016-2-16 14:05:40
感谢分享,对学习django很有帮助。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条