做者: 何全,github地址: https://github.com/hequan2017 QQ交流羣: 620176501html
經過此教程完成從零入門,可以獨立編寫一個簡單的CMDB系統。前端
目前主流的方法開發方式,分爲2種:mvc 和 mvvc方式。本教程爲 mvc 方式,即 django負責渲染html。後面會推出 mvvc(先後端分離)的入門教程。html5
教程項目地址: https://github.com/hequan2017/husky/git
教程文檔地址: https://github.com/hequan2017/husky/tree/master/docgithub
pycharm : 菜單欄 tools --> 選擇 run manage.py taskajax
manage.py@husky > startapp asset ##建立 asset appdjango
具體內容請看實際頁面,下面只是把重點代碼 進行展現。json
import sys INSTALLED_APPS = [ "asset", ]
from django.db import models class Ecs(models.Model): TYPE_CHOICES = ( ('阿里雲', '阿里雲'), ('騰訊雲', '騰訊雲'), ('華爲雲', '華爲雲'), ('亞馬遜', '亞馬遜'), ('其餘', '其餘'), (None,None), ) hostname = models.CharField(max_length=96, verbose_name='主機名', blank=True, null=True, ) type = models.CharField(choices=TYPE_CHOICES, max_length=16, verbose_name='主機類型', blank=True, null=True, ) instance_id = models.CharField(max_length=64, verbose_name='實例ID', unique=True) instance_name = models.CharField(max_length=96, verbose_name='標籤', blank=True, null=True, ) os_name = models.CharField(max_length=64, verbose_name='系統版本', blank=True, null=True, ) cpu = models.IntegerField(verbose_name='CPU', blank=True, null=True) memory = models.IntegerField(verbose_name='內存', blank=True, null=True) private_ip = models.GenericIPAddressField(verbose_name='內網IP', blank=True, null=True) public_ip = models.GenericIPAddressField(verbose_name='外網IP', blank=True, null=True) c_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='建立時間', blank=True) u_time = models.DateTimeField(auto_now=True, null=True, verbose_name='更新時間', blank=True) class Meta: db_table = "ecs" verbose_name = "主機" verbose_name_plural = verbose_name def __str__(self): return self.hostname
pycharm : 菜單欄 tools --> 選擇 run manage.py taskbootstrap
makemigrations 生成數據文件後端
migrate 根據文件,執行生成表結構
from django import forms from asset.models import Ecs class EcsForm(forms.ModelForm): class Meta: model = Ecs fields = '__all__' widgets = { 'type': forms.Select( attrs={'class': 'select2', 'data-placeholder': '----請選擇環境----'}), } help_texts = { 'type': '* 請選擇 資產所在平臺.', } def clean_type(self): """ 自定義驗證 :return: """ type = self.cleaned_data['type'] return type
from django.contrib import admin from asset.models import Ecs admin.site.register(Ecs)
from django import template from django.apps import apps from asset.models import Ecs register = template.Library() @register.filter(name='ecs_model_choices') def ecs_model_choices(model_name, choice_name): asset_app = apps.get_app_config('assets') return getattr(asset_app.get_model(model_name), choice_name) @register.filter(name='ecs_type_choices') def ecs_type_choices(value): return Ecs.TYPE_CHOICES
class EcsCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Ecs 建立 """ permission_required = ('asset.add_ecs',) model = Ecs form_class = EcsForm template_name = 'asset/ecs-create.html' success_url = reverse_lazy('asset:ecs-list') def get_context_data(self, **kwargs): context = {} if '__next__' in self.request.POST: # 爲了獲取 點擊本頁以前的 瀏覽網頁 context['i__next__'] = self.request.POST['__next__'] else: try: context['i__next__'] = self.request.META['HTTP_REFERER'] except Exception as e: logger.error(e) kwargs.update(context) return super().get_context_data(**kwargs) def get_success_url(self): return self.request.POST['__next__'] def form_valid(self, form): # 保存結果 能夠進行 手動 修改 再保存 obj = form.save(commit=False) obj.save() return super().form_valid(form) def form_invalid(self, form): print(form.errors) """If the form is invalid, render the invalid form.""" return self.render_to_response(self.get_context_data(form=form))
from django.urls import path from asset import views app_name = "asset" urlpatterns = [ path('ecs-create', views.EcsCreateView.as_view(), name='ecs-create') ]
<script type="text/JavaScript"> $.ajaxSetup({headers: {"X-CSRFToken": '{{ csrf_token }}'}}); </script> <script> $(document).ready(function () { $('.dataTables-example').DataTable({ "oLanguage": { "sLengthMenu": "每頁顯示 _MENU_ 條記錄", "sZeroRecords": "對不起,查詢不到任何相關數據", "sInfo": "當前顯示 _START_ 到 _END_ 條,共 _TOTAL_條記錄", "sInfoEmtpy": "找不到相關數據", "sInfoFiltered": " 數據表中共爲 _MAX_ 條記錄", "sProcessing": "正在加載中...", "sSearch": "搜索", "oPaginate": { "sFirst": "第一頁", "sPrevious": " 上一頁 ", "sNext": " 下一頁 ", "sLast": " 最後一頁 " } }, dom: '<"html5buttons"B>lTfgitp,', buttons: [ 'copy', 'csv', 'excel' ] }); $('.dataTables-code').DataTable({ "oLanguage": { "sLengthMenu": "每頁顯示 _MENU_ 條記錄", "sZeroRecords": "對不起,查詢不到任何相關數據", "sInfo": "當前顯示 _START_ 到 _END_ 條,共 _TOTAL_條記錄", "sInfoEmtpy": "找不到相關數據", "sInfoFiltered": " 數據表中共爲 _MAX_ 條記錄", "sProcessing": "正在加載中...", "sSearch": "搜索", "oPaginate": { "sFirst": "第一頁", "sPrevious": " 上一頁 ", "sNext": " 下一頁 ", "sLast": " 最後一頁 " } }, bFilter: false, "order": [[1, 'desc']], "info": false,//是否顯示頁腳信息 destroy: true, "ordering": false, dom: '<"html5buttons"B>lTfgitp,', buttons: [], lengthMenu: [[-1], ["所有"]], "paging": false, // 禁止分頁 }); }); $(function () { $(".select2").select2(); }); </script>
<ul class="nav nav-second-level"> {% if perms.asset.add_ecs %} 權限判斷 <li class="ecs-create"> <a href="{% url "asset:ecs-create" %}">添加資產</a> ## 頁面跳轉 </li> {% endif %} {% if perms.asset.view_ecs %} <li class="ecs-list"> <a href="{% url "asset:ecs-list" %}">資產列表</a> </li> {% endif %} </ul>
<form enctype="multipart/form-data" method="post" class="form-horizontal" action=""> {% csrf_token %} {% if form.non_field_errors %} <div class="alert alert-danger" style="margin: 20px auto 0px"> {{ form.non_field_errors }} </div> {% endif %} <div class="form-group"> <div class="col-sm-10 col-sm-offset-0"> <h3>基本</h3> {% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.instance_id layout="horizontal" %} {% bootstrap_field form.instance_name layout="horizontal" %} {% bootstrap_field form.os_name layout="horizontal" %} {% bootstrap_field form.cpu layout="horizontal" %} {% bootstrap_field form.memory layout="horizontal" %} {% bootstrap_field form.private_ip layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %} </div> </div> <input type="hidden" name="__next__" value="{{ i__next__ }}"> <div class="hr-line-dashed"></div> <div class="form-group"> <div class="col-sm-4 col-sm-offset-3"> {% bootstrap_button "保存" button_type="submit" button_class="btn-primary" %} <a class="btn btn-white" href="{{ i__next__ }}">取消</a> </div> </div> </form> {% block footer-js %} <script> 控制左側導航展開 window.onload = function () { $(".asset").addClass("active"); $(".ecs-create").addClass("active"); } </script> {% endblock %}
def get_list(function): """ 列表頁面 獲取 搜索 :param function: self.model :return: """ @wraps(function) def wrapped(self): # user = self.request.user # groups = [x['name'] for x in self.request.user.groups.values()] # request_type = self.request.method # model = str(self.model._meta).split(".")[1] filter_dict = {} not_list = ['page', 'order_by', 'csrfmiddlewaretoken'] for k, v in dict(self.request.GET).items(): if [i for i in v if i != ''] and (k not in not_list): if '__in' in k: filter_dict[k] = v else: filter_dict[k] = v[0] self.filter_dict = filter_dict self.queryset = self.model.objects.filter(**filter_dict).order_by('-id') order_by_val = self.request.GET.get('order_by', '') if order_by_val: self.queryset = self.queryset.order_by(order_by_val) if self.queryset else self.queryset result = function(self) return result return wrapped class EcsListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): permission_required = ('asset.view_ecs',) template_name = 'asset/ecs-list.html' model = Ecs queryset = Ecs.objects.get_queryset().order_by('-id') @get_list ## 處理查詢 def get_context_data(self, **kwargs): try: page = self.request.GET.get('page', 1) except PageNotAnInteger as e: page = 1 p = Paginator(self.queryset, getattr(settings, 'DISPLAY_PER_PAGE'), request=self.request) ecs_list = p.page(page) context = { "ecs_list": ecs_list, "filter_dict": self.filter_dict # 把查詢條件返回給前端 } kwargs.update(context) return super().get_context_data(**kwargs)
path('ecs-list', views.EcsListView.as_view(), name='ecs-list'),
<div class="table-responsive"> <form class="form-horizontal " method="post"> {% csrf_token %} <table class="table table-striped table-bordered table-hover dataTables-code"> <thead> <tr> <th>主機名</th> <th>主機類型</th> <th>實例ID</th> <th>標籤</th> <th>系統版本</th> <th>內網IP</th> <th {% if request.GET.order_by == "-c_time" %} class="sort_asc_png" onclick="window.location.href='?order_by=c_time'" {% elif request.GET.order_by == "c_time" %} class="sort_desc_png" onclick="window.location.href='?order_by=-c_time'" {% else %} class="sort_both_png" onclick="window.location.href='?order_by=c_time'" {% endif %}>建立時間 </th> <th>操做</th> </tr> </thead> <tbody> {% for row in ecs_list.object_list %} <tr class="gradeA" id="{{ row.id }}" name="{{ row.hostname }}"> <td class="center"> <div class="">{{ row.hostname }}</div> </td> <td class="center"> <div class="">{{ row.get_type_display }}</div> </td> <td class="center"> <div class="">{{ row.instance_id }}</div> </td> <td class="center"> <div class="">{{ row.instance_name }}</div> </td> <td class="center"> <div class="">{{ row.os_name }}</div> </td> <td class="center"> <div class="">{{ row.private_ip }}</div> </td> <td class="center"> <div class="">{{ row.c_time | date:'Y-m-d' }}</div> </td> <td> {% if perms.asset.view_ecs %} <a class="btn btn-success btn-xs " href="{% url "asset:ecs-detail" pk=row.id %}">詳情</a> {% endif %} {% if perms.asset.change_ecs %} <a class="btn btn-primary btn-xs " href="{% url "asset:ecs-update" pk=row.id %}">編輯</a> {% endif %} {% if perms.asset.delete_ecs %} <a class="btn btn-danger btn-xs ecs-delete" href="#">刪除</a> {% endif %} </td> </tr> {% endfor %} </tbody> </table> <div style="text-align:center;"> <nav class="pagination"> <li><a href="{% url "asset:ecs-list" %}?page=1">首頁 </a></li> {% if ecs_list.has_previous %} <li class="long"><a href="?{{ ecs_list.previous_page_number.querystring }}">上一頁</a> </li> {% endif %} {% for page in ecs_list.pages %} {% if page %} {% ifequal page ecs_list.number %} <li class="active" ><a style="background-color: #E0E0E0" href="?{{ page.querystring }}">{{ page }}</a> </li> {% else %} <li><a href="?{{ page.querystring }}" class="page">{{ page }}</a> </li> {% endifequal %} {% else %} <li class="none"><a href="">...</a></li> {% endif %} {% endfor %} {% if ecs_list.has_next %} <li class="long"><a href="?{{ ecs_list.next_page_number.querystring }}">下一頁</a> </li> {% endif %} <li> <a href="{% url "asset:ecs-list" %}?page={{ ecs_list.paginator.num_pages }}">尾頁 </a> </li> <li><span style="color: #0a0a0a">總: {{ ecs_list.paginator.num_pages }} 頁</span> </li> <li><span style="color: #0a0a0a"> 數量: {{ ecs_list.paginator.count }}</span> </li> </nav> </div> </form> </div> {% block footer-js %} <script> window.onload = function () { // 固定左邊導航欄 $(".asset").addClass("active"); $(".ecs-list").addClass("active"); }; var filter_dict = {{ filter_dict | safe}}; // 搜索完以後,還能保存搜索項目 $("#hostname").val(filter_dict['hostname__icontains']); $("#type").val(filter_dict['type']); </script>
class EcsUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): permission_required = ('asset.change_ecs',) model = Ecs form_class = EcsForm template_name = 'asset/ecs-create.html' success_url = reverse_lazy('asset:ecs-list') def get_context_data(self, **kwargs): context = {} if '__next__' in self.request.POST: # 爲了獲取 點擊本頁以前的 瀏覽網頁 context['i__next__'] = self.request.POST['__next__'] else: try: context['i__next__'] = self.request.META['HTTP_REFERER'] except Exception as e: logger.error(e) kwargs.update(context) return super().get_context_data(**kwargs) def get_success_url(self): return self.request.POST['__next__']
path('ecs-update-<int:pk>', views.EcsUpdateView.as_view(), name='ecs-update'),
{% if perms.asset.change_ecs %} <a class="btn btn-primary btn-xs " href="{% url "asset:ecs-update" pk=row.id %}">編輯</a> {% endif %}
class EcsDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View): permission_required = ('asset.delete_ecs',) model = Ecs def post(self, request): ret = {'status': True, 'error': None, } nid = self.request.POST.get('nid', None) self.model.objects.get(id=nid).delete() return HttpResponse(json.dumps(ret))
path('ecs-delete', views.EcsDeleteView.as_view(), name='ecs-delete'),
templates.py/asset/ecs-list.html
{% if perms.asset.delete_ecs %} <a class="btn btn-danger btn-xs ecs-delete" href="#">刪除</a> {% endif %}
$(function () { $(document).on('click', '.ecs-delete', function () { var id = $(this).parent().parent().attr('id'); var name = $(this).parent().parent().attr('name'); swal({ title: "你肯定刪除", text: name, type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "肯定", cancelButtonText: "取消", closeOnConfirm: false }, function () { $.ajax({ url: "{% url 'asset:ecs-delete' %}", type: 'POST', data: {'nid': id}, success: function (data) { var obj = JSON.parse(data); if (obj.status) { swal({title: "刪除", text: "已成功刪除", type: "success"}, function () { window.location.reload(); }) } else { swal("錯誤", "刪除" + "[ " + obj.error + " ]" + "遇到錯誤", "error"); } } }); }); }); });
class EcsDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): permission_required = ('asset.view_ecs',) model = Ecs form_class = EcsForm template_name = 'asset/ecs-detail.html' def get_context_data(self, **kwargs): pk = self.kwargs.get(self.pk_url_kwarg, None) context = { "ecs": self.model.objects.get(id=pk), "nid": pk } kwargs.update(context) return super().get_context_data(**kwargs)
path('ecs-detail-<int:pk>', views.EcsDetailView.as_view(), name='ecs-detail'),
<table class="table"> <tbody> <tr> <td style=" border-top: none !important;" width="20%">主機類型:</td> <td style=" border-top: none !important;"><b>{{ ecs.get_type_display }}</b> </td> </tr> <tr> <td width="20%">實例ID:</td> <td><b>{{ ecs.instance_id }}</b> </td> </tr> <tr> <td width="20%">標籤:</td> <td><b>{{ ecs.instance_name }}</b> </td> </tr> <tr> <td width="20%">系統版本:</td> <td><b>{{ ecs.os_name }}</b> </td> </tr> <tr> <td width="20%">CPU:</td> <td><b>{{ ecs.cpu }}</b> </td> </tr> <tr> <td width="20%">內存:</td> <td><b>{{ ecs.memory }}</b> </td> </tr> <tr> <td width="20%">內網IP:</td> <td><b>{{ ecs.private_ip }}</b> </td> </tr> <tr> <td width="20%">外網IP:</td> <td><b>{{ ecs.public_ip }}</b> </td> </tr> <tr> <td width="20%">建立時間:</td> <td><b>{{ ecs.c_time }}</b> </td> </tr> <tr> <td width="20%">更新時間:</td> <td><b>{{ ecs.u_time }}</b> </td> </tr> </tbody> </table>