您好,登錄后才能下訂單哦!
作者: 何全,github地址: https://github.com/hequan2017 QQ交流群: 620176501
通過此教程完成從零入門,能夠獨立編寫一個簡單的CMDB系統(tǒng)。
目前主流的方法開發(fā)方式,分為2種:mvc 和 mvvc方式。本教程為 mvc 方式,即 django負(fù)責(zé)渲染html。后面會推出 mvvc(前后端分離)的入門教程。
教程項目地址: https://github.com/hequan2017/husky/
教程文檔地址: https://github.com/hequan2017/husky/tree/master/doc
pycharm : 菜單欄 tools --> 選擇 run manage.py task
manage.py@husky > startapp asset ##創(chuàng)建 asset app
具體內(nèi)容請看實際頁面,下面只是把重點代碼 進行展示。
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='標(biāo)簽', blank=True, null=True, )
os_name = models.CharField(max_length=64, verbose_name='系統(tǒng)版本', blank=True, null=True, )
cpu = models.IntegerField(verbose_name='CPU', blank=True, null=True)
memory = models.IntegerField(verbose_name='內(nèi)存', blank=True, null=True)
private_ip = models.GenericIPAddressField(verbose_name='內(nèi)網(wǎng)IP', blank=True, null=True)
public_ip = models.GenericIPAddressField(verbose_name='外網(wǎng)IP', blank=True, null=True)
c_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='創(chuàng)建時間', 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 task
makemigrations 生成數(shù)據(jù)文件
migrate 根據(jù)文件,執(zhí)行生成表結(jié)構(gòu)
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': '----請選擇環(huán)境----'}),
}
help_texts = {
'type': '* 請選擇 資產(chǎn)所在平臺.',
}
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 創(chuàng)建
"""
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: # 為了獲取 點擊本頁之前的 瀏覽網(wǎng)頁
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): # 保存結(jié)果 可以進行 手動 修改 再保存
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": "對不起,查詢不到任何相關(guān)數(shù)據(jù)",
"sInfo": "當(dāng)前顯示 _START_ 到 _END_ 條,共 _TOTAL_條記錄",
"sInfoEmtpy": "找不到相關(guān)數(shù)據(jù)",
"sInfoFiltered": " 數(shù)據(jù)表中共為 _MAX_ 條記錄",
"sProcessing": "正在加載中...",
"sSearch": "搜索",
"oPaginate": {
"sFirst": "第一頁",
"sPrevious": " 上一頁 ",
"sNext": " 下一頁 ",
"sLast": " 最后一頁 "
}
},
dom: '<"html5buttons"B>lTfgitp,',
buttons: [ 'copy', 'csv', 'excel' ]
});
$('.dataTables-code').DataTable({
"oLanguage": {
"sLengthMenu": "每頁顯示 _MENU_ 條記錄",
"sZeroRecords": "對不起,查詢不到任何相關(guān)數(shù)據(jù)",
"sInfo": "當(dāng)前顯示 _START_ 到 _END_ 條,共 _TOTAL_條記錄",
"sInfoEmtpy": "找不到相關(guān)數(shù)據(jù)",
"sInfoFiltered": " 數(shù)據(jù)表中共為 _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 %} 權(quán)限判斷
<li class="ecs-create">
<a href="{% url "asset:ecs-create" %}">添加資產(chǎn)</a> ## 頁面跳轉(zhuǎn)
</li>
{% endif %}
{% if perms.asset.view_ecs %}
<li class="ecs-list">
<a href="{% url "asset:ecs-list" %}">資產(chǎn)列表</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" >
{{ form.non_field_errors }}
</div>
{% endif %}
<div class="form-group">
<div class="col-sm-10 col-sm-offset-0">
<h4>基本</h4>
{% 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> 控制左側(cè)導(dǎo)航展開
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>標(biāo)簽</th>
<th>系統(tǒng)版本</th>
<th>內(nèi)網(wǎng)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 %}>創(chuàng)建時間
</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 >
<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 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
>總: ?{{ ecs_list.paginator.num_pages }} 頁</span>
</li>
<li><span
> 數(shù)量: {{ ecs_list.paginator.count }}</span>
</li>
</nav>
</div>
</form>
</div>
{% block footer-js %}
<script>
window.onload = function () { // 固定左邊導(dǎo)航欄
$(".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: # 為了獲取 點擊本頁之前的 瀏覽網(wǎng)頁
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 width="20%">主機類型:</td>
<td ><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%">標(biāo)簽:</td>
<td><b>{{ ecs.instance_name }}</b>
</td>
</tr>
<tr>
<td width="20%">系統(tǒng)版本:</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%">內(nèi)存:</td>
<td><b>{{ ecs.memory }}</b>
</td>
</tr>
<tr>
<td width="20%">內(nèi)網(wǎng)IP:</td>
<td><b>{{ ecs.private_ip }}</b>
</td>
</tr>
<tr>
<td width="20%">外網(wǎng)IP:</td>
<td><b>{{ ecs.public_ip }}</b>
</td>
</tr>
<tr>
<td width="20%">創(chuàng)建時間:</td>
<td><b>{{ ecs.c_time }}</b>
</td>
</tr>
<tr>
<td width="20%">更新時間:</td>
<td><b>{{ ecs.u_time }}</b>
</td>
</tr>
</tbody>
</table>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。