Introduction to Django CMDB system
Preface
Author: he Quan, github address: https://github.com/hequan2017 QQ communication group: 620176501
Through this tutorial, you can start from scratch and write a simple CMDB system independently.
At present, there are two main development methods: mvc and mvvc. This tutorial is in mvc mode, that is, django is responsible for rendering html. The introduction of mvvc (front and back separation) will be introduced later.
Tutorial project address: https://github.com/hequan2017/husky/
Tutorial document address: https://github.com/hequan2017/husky/tree/master/doc
Explain
- cbv uses classes to process requests in views (in this way, it will be more abstract, but it will be simpler)
- fbv uses functions to process requests in views
Foundation setup
pycharm: select run manage.py task from tools
Manage.py @ husky > startapp asset create asset app
Please refer to the actual page for details. The following is just the key code to show.
- settings.py add system configuration
import sys INSTALLED_APPS = [ "asset", ]
- asset/modes.py
from django.db import models class Ecs(models.Model): TYPE_CHOICES = ( ('Ali cloud', 'Ali cloud'), ('Tencent cloud', 'Tencent cloud'), ('Hua Wei Yun', 'Hua Wei Yun'), ('Amazon', 'Amazon'), ('Other', 'Other'), (None,None), ) hostname = models.CharField(max_length=96, verbose_name='host name', blank=True, null=True, ) type = models.CharField(choices=TYPE_CHOICES, max_length=16, verbose_name='Host type', blank=True, null=True, ) instance_id = models.CharField(max_length=64, verbose_name='Example ID', unique=True) instance_name = models.CharField(max_length=96, verbose_name='Label', blank=True, null=True, ) os_name = models.CharField(max_length=64, verbose_name='System version', blank=True, null=True, ) cpu = models.IntegerField(verbose_name='CPU', blank=True, null=True) memory = models.IntegerField(verbose_name='Memory', blank=True, null=True) private_ip = models.GenericIPAddressField(verbose_name='Intranet IP', blank=True, null=True) public_ip = models.GenericIPAddressField(verbose_name='Extranet IP', blank=True, null=True) c_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='Creation time', blank=True) u_time = models.DateTimeField(auto_now=True, null=True, verbose_name='Update time', blank=True) class Meta: db_table = "ecs" verbose_name = "Host" verbose_name_plural = verbose_name def __str__(self): return self.hostname
pycharm: select run manage.py task from tools
makemigrations generate data files
migrate executes the generate table structure according to the file
- asset/form.py
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': '----Please select environment----'}), } help_texts = { 'type': '* Please select the platform of the asset.', } def clean_type(self): """ //Custom validation :return: """ type = self.cleaned_data['type'] return type
- asset/admin.py
from django.contrib import admin from asset.models import Ecs admin.site.register(Ecs)
- Custom label processing
- asset/templatetags/asset_filter.py
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
increase
- asset/views.py
class EcsCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Ecs Establish """ 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: # To get the browsing page before clicking this page 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): # The saved result can be modified manually and then saved 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))
- asset/urls.py
from django.urls import path from asset import views app_name = "asset" urlpatterns = [ path('ecs-create', views.EcsCreateView.as_view(), name='ecs-create') ]
- templates/base/_js.html
<script type="text/JavaScript"> $.ajaxSetup({headers: {"X-CSRFToken": '{{ csrf_token }}'}}); </script> <script> $(document).ready(function () { $('.dataTables-example').DataTable({ "oLanguage": { "sLengthMenu": "Display per page _MENU_ Bar record", "sZeroRecords": "Sorry, we can't find any relevant data", "sInfo": "Current display _START_ reach _END_ A total of _TOTAL_Bar record", "sInfoEmtpy": "No data found", "sInfoFiltered": " Data sheet is _MAX_ Bar record", "sProcessing": "Loading...", "sSearch": "search", "oPaginate": { "sFirst": "First page", "sPrevious": " Previous page ", "sNext": " next page ", "sLast": " last page " } }, dom: '<"html5buttons"B>lTfgitp,', buttons: [ 'copy', 'csv', 'excel' ] }); $('.dataTables-code').DataTable({ "oLanguage": { "sLengthMenu": "Display per page _MENU_ Bar record", "sZeroRecords": "Sorry, we can't find any relevant data", "sInfo": "Current display _START_ reach _END_ A total of _TOTAL_Bar record", "sInfoEmtpy": "No data found", "sInfoFiltered": " Data sheet is _MAX_ Bar record", "sProcessing": "Loading...", "sSearch": "search", "oPaginate": { "sFirst": "First page", "sPrevious": " Previous page ", "sNext": " next page ", "sLast": " last page " } }, bFilter: false, "order": [[1, 'desc']], "info": false,//Show footer information or not destroy: true, "ordering": false, dom: '<"html5buttons"B>lTfgitp,', buttons: [], lengthMenu: [[-1], ["whole"]], "paging": false, // No paging }); }); $(function () { $(".select2").select2(); }); </script>
- templates/base/_nav.html
<ul class="nav nav-second-level"> {% if perms.asset.add_ecs %} Authority judgement <li class="ecs-create"> <a href="{% url "asset:ecs-create" %}">Add assets</a> ## Page skipping </li> {% endif %} {% if perms.asset.view_ecs %} <li class="ecs-list"> <a href="{% url "asset:ecs-list" %}">List of assets</a> </li> {% endif %} </ul>
- templates/asset/ecs-create.html
<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>basic</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 "Preservation" button_type="submit" button_class="btn-primary" %} <a class="btn btn-white" href="{{ i__next__ }}">cancel</a> </div> </div> </form> {% block footer-js %} <script> Control left navigation deployment window.onload = function () { $(".asset").addClass("active"); $(".ecs-create").addClass("active"); } </script> {% endblock %}
list
- asset/views.py
def get_list(function): """ //List page get search :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 ## Processing query 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 # Return the query criteria to the front end } kwargs.update(context) return super().get_context_data(**kwargs)
- asset/urls.py
path('ecs-list', views.EcsListView.as_view(), name='ecs-list'),
- templates.py/asset/ecs-list.html
<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>host name</th> <th>Host type</th> <th>Example ID</th> <th>Label</th> <th>System version</th> <th>Intranet 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 %}>Creation time </th> <th>operation</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 %}">details</a> {% endif %} {% if perms.asset.change_ecs %} <a class="btn btn-primary btn-xs " href="{% url "asset:ecs-update" pk=row.id %}">edit</a> {% endif %} {% if perms.asset.delete_ecs %} <a class="btn btn-danger btn-xs ecs-delete" href="#> > delete </a> {% endif %} </td> </tr> {% endfor %} </tbody> </table> <div style="text-align:center;"> <nav class="pagination"> <li><a href="{% url "asset:ecs-list" %}?page=1">home page </a></li> {% if ecs_list.has_previous %} <li class="long"><a href="?{{ ecs_list.previous_page_number.querystring }}">Previous page</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 }}">next page</a> </li> {% endif %} <li> <a href="{% url "asset:ecs-list" %}?page={{ ecs_list.paginator.num_pages }}">Tail page </a> </li> <li><span style="color: #0a0a0a "> total: {{ECS [u list. Pager. Num [u pages}}} page</span> </li> <li><span style="color: #0a0a0a "> quantity: {ECS [list. Pager. Count}}}}</span> </li> </nav> </div> </form> </div> {% block footer-js %} <script> window.onload = function () { // Fix left navigation bar $(".asset").addClass("active"); $(".ecs-list").addClass("active"); }; var filter_dict = {{ filter_dict | safe}}; // After the search, you can also save the search items $("#hostname").val(filter_dict['hostname__icontains']); $("#type").val(filter_dict['type']); </script>
To update
- asset/views.py
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: # To get the browsing page before clicking this page 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__']
- asset/urls.py
path('ecs-update-<int:pk>', views.EcsUpdateView.as_view(), name='ecs-update'),
- templates.py/asset/ecs-create.html create a page while updating the page
{% if perms.asset.change_ecs %} <a class="btn btn-primary btn-xs " href="{% url "asset:ecs-update" pk=row.id %}">edit</a> {% endif %}
delete
- asset/views.py
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))
- asset/urls.py
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="#> > delete </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: "Are you sure to delete", text: name, type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "Determine", cancelButtonText: "cancel", 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: "delete", text: "Successfully deleted", type: "success"}, function () { window.location.reload(); }) } else { swal("error", "delete" + "[ " + obj.error + " ]" + "Encountered errors", "error"); } } }); }); }); });
details
- asset/views.py
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)
- asset/urls.py
path('ecs-detail-<int:pk>', views.EcsDetailView.as_view(), name='ecs-detail'),
- templates.py/asset/ecs-list.html
<table class="table"> <tbody> <tr> <td style=" border-top: none !important;" width="20%">Host type:</td> <td style=" border-top: none !important;"><b>{{ ecs.get_type_display }}</b> </td> </tr> <tr> <td width="20%">Example ID:</td> <td><b>{{ ecs.instance_id }}</b> </td> </tr> <tr> <td width="20%">Label:</td> <td><b>{{ ecs.instance_name }}</b> </td> </tr> <tr> <td width="20%">System version:</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%">Memory:</td> <td><b>{{ ecs.memory }}</b> </td> </tr> <tr> <td width="20%">Intranet IP:</td> <td><b>{{ ecs.private_ip }}</b> </td> </tr> <tr> <td width="20%">Extranet IP:</td> <td><b>{{ ecs.public_ip }}</b> </td> </tr> <tr> <td width="20%">Creation time:</td> <td><b>{{ ecs.c_time }}</b> </td> </tr> <tr> <td width="20%">Update time:</td> <td><b>{{ ecs.u_time }}</b> </td> </tr> </tbody> </table>