datatables 行分组信息展开与折叠的功能实现_Django实战2-自动化运维之配置管理-05:字典管理功能实现...

707ca0e8cec23ce1ce53dbc40e8a95c2.png
本节内容主要实现CMDB基础数据管理功能的实现,文档中会介绍到datatables后端分页实现和过滤查询功能的实现。在Django实战1中已经自定义了 添加、修改等自定义类视图,本节中可以直接使用。

1、字典管理页面实现

首先来实现字段管理的基础管理页面,针对字典的一系列操作,都将在这个页面上完成。

1.1 字典管理视图实现

新建sandboxMP/apps/cmdb/views_code.py,写入如下内容:

from django.views.generic import TemplateView

from system.mixin import LoginRequiredMixin
from custom import BreadcrumbMixin
from .models import Code


class CodeView(LoginRequiredMixin, BreadcrumbMixin, TemplateView):
    template_name = 'cmdb/code.html'

    def get_context_data(self):
        self.kwargs['code_parent'] = Code.objects.filter(parent=None)
        return super().get_context_data(**self.kwargs)

在上面视图中,通过重写get_context_data方法来返回字典数据中所有父类为None的上下文,用来作为过滤查询时的选择项。

1.2 字典管理访问URL

打开,sandboxMP/apps/cmdb/urls.py,添加如下内容:

from . import views_code

urlpatterns = [
    '''原有内容省略'''
    path('portal/code/', views_code.CodeView.as_view(), name='portal-code'),
]

根据权限管理中URL定义规则,这里将字段管理功能URL放到portal分组中;在实战2第三节中已经说明,新的URL都需要添加到权限系统。

1.3 字典管理模板配置

修建模板文件:sandboxMP/templates/cmdb/code.html,内容如下:

{% extends "base-left.html" %}
{% load staticfiles %}

{% block css %}
    <link rel="stylesheet" href="{% static 'plugins/datatables/jquery.dataTables.min.css' %}">
    <link rel="stylesheet" href="{% static 'js/plugins/layer/skin/layer.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
{% endblock %}

{% block content %}

    <!-- Main content -->
  <section class="content">
    <div id="devlist">
        <div class="box box-primary" id="liebiao">
            <div class="box-header">
                <div class="btn-group pull-left">
                    <button type="button" id="btnRefresh" class="btn btn-default">
                        <i class="glyphicon glyphicon-repeat"></i>刷新
                    </button>
                </div>
                <div class="btn-group pull-left">&nbsp</div>
                <div class="btn-group pull-left">
                    <button type="button" id="btnCreate" class="btn btn-default">
                        <i class="glyphicon glyphicon-plus"></i>新增
                    </button>

                </div>
                <div class="btn-group pull-left">&nbsp</div>
                <div class="btn-group pull-left">
                    <button type="button" id="btnDelete" class="btn btn-default">
                        <i class="glyphicon glyphicon-trash"></i>删除
                    </button>
                </div>
                <div class="pull-right">
                    <form class="form-inline" id="queryForm">
                       <div class="form-group searchArea margin-r-5 margin-top-5">
                        <label>字典分类:</label>
                        <select class="form-control inputText select2" name="parent" id="parent">
                            <option style='text-align:center' value="">---所有---</option>
                            {% for code in code_parent %}
                            <option value={{ code.key}}>{{ code.value }}</option>
                            {% endfor %}
                        </select>
                       </div>
                    </form>
                </div>
            </div>

            <div class="box-body">
                <table id="dtbList" class="display" cellspacing="0" width="100%">
                    <thead>
                    <tr valign="middle">
                        <th><input type="checkbox" id="checkAll"></th>
                        <th>ID</th>
                        <th>KEY</th>
                        <th>VALUE</th>
                        <th>所属</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
                <br> <br>
            </div>
        </div>
    </div>

  </section>

    <!-- /.content -->

{% endblock %}


{% block javascripts %}

<script src="{% static 'plugins/datatables/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'plugins/datatables/dataTables.const-1.js' %}"></script>
<script src="{% static 'js/plugins/layer/layer.js' %}"></script>
<script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>

{% endblock %}

==按下CRTL + S 保存并上传项目文件==

1.4 权限管理配置

URL数据内容:

a53dd5459fead13f2b4fa10d3c5fd833.png

运行项目,登陆系统,访问菜单管理页【系统】→ 【权限管理】→ 【菜单管理】,按照上面URL内容,将数据添加到菜单管理中:

http://172.16.3.100:8000/system/rbac/menu/

访问角色管理页【系统】→ 【权限管理】→ 【角色管理】,选择【系统管理】后面第三个树形按钮配置权限信息,将新配置的URL授权给角色组:系统管理,选择好菜单后(全选)点【生成】按钮。

http://172.16.3.100:8000/system/rbac/role/

完成权限配置就可以在页面中访问【配置管理】→ 【平台设置】→ 【字典管理】。

每一个视图功能的访问都需要做对应的权限配置,在后面不再列出添加和授权步骤。

2 字典添加功能实现

2.1 字典添加Form配置

打开文件:sandboxMP/apps/cmdb/forms.py,写入如下内容:

from django import forms

from .models import Code


class CodeCreateForm(forms.ModelForm):
    class Meta:
        model = Code
        fields = '__all__'

        error_messages = {
            'key': {'required': 'key不能为空'},
            'value': {'required': 'value不能为空'}
        }

    def clean(self):
        cleaned_data = super(CodeCreateForm, self).clean()
        key = cleaned_data.get('key')
        value = cleaned_data.get('value')

        if Code.objects.filter(key=key).count():
            raise forms.ValidationError('key:{}已存在'.format(key))

        if Code.objects.filter(value=value).count():
            raise forms.ValidationError('value: {}已存在'.format(value))

通过CodeCreateForm可以对输入数据的有效性进行验证,同时避免内容重复。

CTRL + S 保存并上传。

2.2 字典添加视图实现

打开:sandboxMP/apps/cmdb/views_code.py,添加CodeCreateView视图:

from custom import SandboxCreateView
from .forms import CodeCreateForm

class CodeCreateView(SandboxCreateView):
    model = Code
    form_class = CodeCreateForm
    template_name_suffix = '_create'

    def get_context_data(self, **kwargs):
        kwargs['code_parent'] = Code.objects.filter(parent=None)
        return super().get_context_data(**kwargs)

字典添加视图,使用了在django实战1中创建的SandboxCreateView,重写get_context_data 返回所有父类code,用于创建时候的选择项。
SandboxCreateView类当初在定义的时候,只返回了执行结果result(True or False),前端根据result结果来提示执行成果或执行失败,这里想要返回form中自定义的错误提示信息,修改sandboxMP/apps/custom.py中SandboxEditViewMixin,内容如下:

import re

class SandboxEditViewMixin:

    def post(self, request, *args, **kwargs):
        res = dict(result=False)
        form = self.get_form()
        if form.is_valid():
            form.save()
            res['result'] = True
        else:
            pattern = '<li>.*?<ul class=.*?><li>(.*?)</li>'
            form_errors = str(form.errors)
            errors = re.findall(pattern, form_errors)
            res['error'] = errors[0]
        return HttpResponse(json.dumps(res), content_type='application/json')

2.4 字典添加URL配置

打开:sandboxMP/apps/cmdb/urls.py,添加新的URL:

urlpatterns = [
    '''原有内容省略'''
    path('portal/code/create/', views_code.CodeCreateView.as_view(), name='portal-code-create'),
]

2.5 模板配置

新建模板文件:sandboxMP/templates/cmdb/code_crate.html,内容如下:

{% extends 'base-layer.html' %}
{% load staticfiles %}
{% block css %}
    <link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
    <!-- iCheck for checkboxes and radio inputs -->
{% endblock %}
{% block main %}
    <div class="box box-danger">
        <form class="form-horizontal" id="addForm" method="post">
            {% csrf_token %}
            <div class="box-body">
                <fieldset>
                    <legend>
                        <h4>新建字典</h4>
                    </legend>

                    <div class="form-group has-feedback">
                        <label class="col-sm-2 control-label">KEY</label>
                        <div class="col-sm-3">
                            <input class="form-control" name="key" type="text"/>
                        </div>
                        <label class="col-sm-2 control-label">VALUE</label>
                        <div class="col-sm-3">
                            <input class="form-control" name="value" type="text" />
                        </div>

                    </div>

                    <div class="form-group has-feedback">
                        <label class="col-sm-2 control-label">父菜单</label>
                        <div class="col-sm-3">
                            <select class="form-control select2" name="parent">
                                <option value=""></option>
                                {% for parent in code_all %}
                                    <option value={{ parent.id }}> {{ parent.value }} </option>
                                {% endfor %}
                            </select>
                        </div>
                        <label class="col-sm-2 control-label">描述信息</label>
                        <div class="col-sm-3">
                            <input class="form-control" id="desc" name="desc" type="text" />
                        </div>
                    </div>

                </fieldset>
            </div>
            <div class="box-footer ">
                <div class="row span7 text-center ">
                    <button type="button" id="btnCancel" class="btn btn-default margin-right ">重置</button>
                    <button type="button" id="btnSave" class="btn btn-info margin-right ">保存</button>
                </div>
            </div>

        </form>
    </div>

{% endblock %}

{% block javascripts %}
    <script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
    <script type="text/javascript">
    $("#btnSave").click(function () {
        var data = $("#addForm").serialize();
        $.ajax({
            type: $("#addForm").attr('method'),
            url: "{% url 'cmdb:portal-code-create' %}",
            data: data,
            cache: false,
            success: function (msg) {
                if (msg.result) {
                    layer.alert('数据保存成功!', {icon: 1}, function (index) {
                        parent.layer.closeAll(); //关闭所有弹窗
                    });
                } else {
                    layer.alert(msg.error, {icon: 5});
                    //$('errorMessage').html(msg.message)
                }
                return;
            }
        });
    });


        /*点取消刷新新页面*/
        $("#btnCancel").click(function () {
            window.location.reload();

        });

        $(function () {
            //Initialize Select2 Elements
            $(".select2").select2();
        });

    </script>

{% endblock %}

注意:在{% block javascripts %}标签中的$("#btnSave")方法在执行成功后,提示信息中通过msg.error来返回form中的错误提示信息。
给字典管理页 添加按钮绑定添加事件,打开sandboxMP/templates/cmdb/code.html,在{% block javascripts %}中的菜单高亮代码段后面添加如下代码:

// 刷新数据
    $("#btnRefresh").click(function () {
        oDataTable.ajax.reload();
    });
    //新建字典
    $("#btnCreate").click(function () {
        layer.open({
            type: 2,
            title: '新增',
            shadeClose: false,
            maxmin: true,
            area: ['800px', '400px'],
            content: "{% url 'cmdb:portal-code-create' %}",
            end: function () {
                //关闭时做的事情
                oDataTable.ajax.reload();
            }
        });
    });

2.6 权限管理配置

请将下面URL数据添加到菜单管理,并授权给【系统管理】角色组:

5727dd49632d97d4472f4d887b3e5c27.png

菜单添加和角色授权过程不再单独写出来了,前面内容已经介绍够了。

配置完授权后就可以通过字典管理页面中的添加按钮来添加新的字典数据:

http://172.16.3.100:8000/cmdb/portal/code/

3 字典数据列表展示

在django实战1中,我们使用datatables通过ajax来获取并展示数据信息,数据分页是使用的datatables的前端分页,本节将介绍使用datatables的后端分页功能。

3.1 自定义数据列表类

创建一个带有分页和过滤查询的数据列表Mixin类,可用于所有基于datatables后端分页的数据,打开sandbox/apps/custom.py,添加如下内容:

from django.views.generic import View
from django.http import JsonResponse
from django.db.models.query import QuerySet
from django.core.exceptions import ImproperlyConfigured


class SandboxMultipleObjectMixin:

    filters = {}
    fields = []
    queryset = None
    model = None
    # 用来获取queryset,下面内容参照了Django通用类视图的基本写法
    def get_queryset(self):
        if self.queryset is not None:
            queryset = self.queryset
            if isinstance(queryset, QuerySet):
                queryset = queryset.all()
        elif self.model is not None:
            queryset = self.model._default_manager.all()
        else:
            raise ImproperlyConfigured(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset."
                % {'cls': self.__class__.__name__}
            )
        return queryset

    def get_datatables_paginator(self, request):
        # 从request中获取datatables需要服务端处理的数据信息,具体内容参照下面知识点介绍
        datatables = request.GET
        draw = int(datatables.get('draw'))
        start = int(datatables.get('start'))
        length = int(datatables.get('length'))
        order_column = datatables.get('order[0][column]')
        order_dir = datatables.get('order[0][dir]')
        order_field = datatables.get('columns[{}][data]'.format(order_column))
        # 使用self.get_queryset方法来获取queryset数据
        queryset = self.get_queryset()
        # 根据datatables传递回来的排序信息进行排序(支持正向和反向排序)
        if order_dir == 'asc':
            queryset = queryset.order_by(order_field)
        else:
            queryset = queryset.order_by('-{0}'.format(order_field))
        # 统计所有数据条目
        record_total_count = queryset.count()
        # 获取过滤字段
        filters = self.get_filters()
        # 获取需要在datatables中展示的字段
        fields = self.get_fields()
        if filters:
            queryset = queryset.filter(**filters)
        if fields:
            queryset = queryset.values(*fields)
        # 过滤后的数据条目
        record_filter_count = queryset.count()
        # 对queryset进行切片操作,只返回当前需要展示的数据
        object_list = queryset[start:(start + length)]

        data = list(object_list)
        # 下面内容是datatables后端分页必须返回的数据,网上有些说明
        return {
            'draw': draw,
            'recordsTotal': record_total_count,
            'recordsFiltered': record_filter_count,
            'data': data,
        }

    def get_filters(self):
        return self.filters

    def get_fields(self):
        return self.fields


class SandboxListView(LoginRequiredMixin, SandboxMultipleObjectMixin, View):
    """
    JsonResponse some json of objects, set by `self.model` or `self.queryset`.
    """
    def get(self, request):
        context = self.get_datatables_paginator(request)
        return JsonResponse(context)

知识点介绍(查看代码中注释部分):
1、datatables 后端分页的请求参数和返回参数详情可以查看下面内容:

http://www.datatables.club/manual/server-side.html

2、JsonResponse(context) 接受字典数据,实现了json.dumps()和 HttpResponse两个功能,对比下SandboxEditViewMixin。

3.2 字典列表视图实现

打开sandboxMP/apps/cmdb/views_code.py,添加CodeListView:

from custom import SandboxListView

class CodeListView(SandboxListView):
    model = Code
    fields = ['id', 'key', 'value', 'parent__value']

    def get(self, request):
        if 'parent' in request.GET and request.GET['parent']:
            self.filters = dict(parent__key=request.GET['parent'])
        return super().get(request)

知识点介绍:
1、CodeListView继承了SandboxListView,通过fields指定需要在列表中展示的字段,其中parent是一个外键,通过parent__value实现多级的取值。
2、重写get方法,从request中获取parent(前端页面中传递的是parent的key)内容,将数据组合成字典赋值给filters,后端会根据filters内容进行数据的过滤。

3.3 字典列表URL

打开sandboxMP/apps/cmdb/urls.py,添加如下内容:

from . import views_code

urlpatterns = [
    '''原有内容省略'''
    path('portal/code/list/', views_code.CodeListView.as_view(), name='portal-code-list'),
]

3.4 模板配置

打开sandboxMP/templates/cmdb/code.html,在{% block javascripts %}下菜单高亮代码段后面添加datatables初始化配置和数据过滤刷新ajax请求的代码段:

// datatables 初始化配置
    var oDataTable = null;
    $(function () {
        oDataTable = initTable();

        function initTable() {
            var oTable = $('#dtbList').DataTable($.extend(true, {},
                DATATABLES_CONSTANT.DATA_TABLES.SERVER_SIDE_OPTION,

                {
                    ajax: {
                        "url": "{% url 'cmdb:portal-code-list' %}",
                        "data": function (d) {
                                d.parent = $("#parent").val();
                            }
                    },
                    columns: [
                        DATATABLES_CONSTANT.DATA_TABLES.COLUMN.CHECKBOX,
                        {
                            data: "id",
                            width: "5%",
                        },
                        {
                            data: "key",
                            //width : "20%",
                        },
                        {
                            data: "value",
                            //width : "20%",
                        },
                        {
                            data: "parent__value",
                            //width : "20%",
                        },
                        {
                            data: "id",
                            width: "10%",
                            bSortable: "false",
                            render: function (data, type, row, meta) {
                                var ret = "";
                                var ret = "<button title='详情' onclick='doUpdate("
                                    + data + ")'><i class='glyphicon glyphicon-pencil'></i></button>";
                                ret = ret + "<button title='删除' onclick='doDelete("
                                    + data + ")'><i class='glyphicon glyphicon-trash'></i></button>";
                                return ret;
                            }
                        }],
                    }));
                return oTable;
            }

        });

    //select2
    $(function () {
        //Initialize Select2 Elements
        $(".select2").select2();
    });

    //过滤刷新接口获取新的数据
    $("#parent").change(function () {
        oDataTable.ajax.reload();
    });

打开sandboxMP/static/plugins/datatables/dataTables.const-1.js,修改下面注释部分内容,把DEFAULT_OPTION 改成 SERVER_SIDE_OPTION

var DATATABLES_CONSTANT = {  

    // datatables常量  
    DATA_TABLES : {  
        SERVER_SIDE_OPTION : { // 把DEFAULT_OPTION改成SERVER_SIDE_OPTION
            oLanguage : {  
                sProcessing : "处理中...",  
                sLengthMenu : "每页 _MENU_ 项",//"显示 _MENU_ 项结果,",  
                sZeroRecords : "没有匹配结果",  
                sInfo : "显示第 _START_ 至 _END_ 项结果(共 _TOTAL_ 项)",
    '''原有内容省略'''

系统中启用了后端分页,所以在初始化datatables表格的时候,使用的是daTables.const-1.js,这个文件中配置了后端分页的一些基本配置,上面代码中注释部分修改的名称只是为了却分前端分页的配置内容,同时在code.html初始化datatables的时候调取的是SERVER_SIDE_OPTION。

CRTL + S 保存并上传。

3.5 权限管理配置

请将下面URL数据添加到菜单管理,并授权给【系统管理】角色组:

a6d021bc6cf64f901f74f03ffddfe78d.png

3.6 后端分页功能测试

到这里后端分页和过滤功能已经完整实现了,将下面的字典数据添加到系统,验证各个功能:

c838414a926c4cf4419dcbbe978daff3.png

运行项目,访问系统字典管理页,通过新增按钮完成上面字典数据的添加工作:

http://172.16.3.100:8000/cmdb/portal/code/

CTRL + F5 刷新页面,在页面中可以看到数据已经自动完成分页,我们和可以设置每页显示的数据条目,可以切换分页获取不同数据,可以通过字典分类进行数据过滤,可以点击表格头部进行数据排序。

3.6 datatables后端分页参数梳理方法

在使用datatables后端分页功能后,很多人面对一堆参数和后端的代码处理逻辑时会有点懵,先浏览一遍上面给的后端分页参数网址,了解参数的基本用途,然后使用Chrome浏览器访问字典管理页面,按F12打开浏览器调试窗口,F5刷新页面,选择Chrome调试窗口中的 Network → ?draw=... → Headers 往下拖在后面可以看到datatables传递给后台的参数。

89c74378d2111e5a66aa4989c4c218ab.png

对照自定义的SandboxMultipleObjectMixin中get_datatables_paginator(self, request)方法中通过request中获取的datattables分页参数,理解参数的具体意义。
也可以切换分页后查看chrome调试敞口中最新访问的?draw=..Headers中start参数的变化。
选择每页显示数据条目,对比下chrome调试敞口中最新访问的?draw=..Headers中length参数的变化。
点击表格头部KEY进行排序,查看chrome调试敞口中最新访问的?draw=..Headers中order[0][column],order[0][dir],columns[2][data]内容,然后再去理解下get_datatables_paginator(self, request)方法中获取参数的内容。

datatables = request.GET
draw = int(datatables.get('draw'))
start = int(datatables.get('start'))
length = int(datatables.get('length'))
order_column = datatables.get('order[0][column]')
order_dir = datatables.get('order[0][dir]')
order_field = datatables.get('columns[{}][data]'.format(order_column))

一定要去做下操作,然后在对比下参数,和后端代码中实现,才能够加深理解。

4 字典更新功能实现

4.1 字典更新Form验证

打开文件:sandboxMP/apps/cmdb/forms.py,添加如下内容:

class CodeUpdateForm(CodeCreateForm):

    def clean(self):
        cleaned_data = self.cleaned_data
        key = cleaned_data.get('key')
        value = cleaned_data.get('value')

        if self.instance:
            matching_code = Code.objects.exclude(pk=self.instance.pk)
            if matching_code.filter(key=key).exists():
                msg = 'key:{} 已经存在'.format(key)
                raise forms.ValidationError(msg)
            if matching_code.filter(value=value).exists():
                msg = 'value:{} 已经存在'.format(value)
                raise forms.ValidationError(msg)

在更新字典数据的时候,同样需要验证输入字段的有效性,这点在CodeCreateForm中已经时间了,避免代码重复,这里直接继承CodeCreateForm。同时重写clean方法,排除当前修改的数据之外保证数据没有重复。

4.2 字典更新视图

字典更新视图可以使用SandboxUpdateView来实现,打开sandboxMP/apps/cmdb/views_code.py,添加CodeUpdateView:

from custom import SandboxUpdateView
from .forms import CodeUpdateForm

class CodeUpdateView(SandboxUpdateView):
    model = Code
    form_class = CodeUpdateForm
    template_name_suffix = '_update'

    def get_context_data(self, **kwargs):
        kwargs['code_parent'] = Code.objects.filter(parent=None)
        return super().get_context_data(**kwargs)

4.3 字典更新URL

打开sandboxMP/apps/cmdb/urls.py,添加新的url:

urlpatterns = [
    '''原有内容省略'''
    path('portal/code/update/', views_code.CodeUpdateView.as_view(), name='portal-code-update'),

4.4 模板配置

新建模板sandboxMP/templates/cmdb/code_update.html:

{% extends 'base-layer.html' %}
{% load staticfiles %}
{% block css %}
    <link rel="stylesheet" href="{% static 'plugins/select2/select2.min.css' %}">
    <!-- iCheck for checkboxes and radio inputs -->
{% endblock %}
{% block main %}
    <div class="box box-danger">
        <form class="form-horizontal" id="addForm" method="post">
        <input type="hidden" name='id' type='text' value="{{ code.id }}"/>
            {% csrf_token %}
            <div class="box-body">
                <fieldset>
                    <legend>
                        <h4>修改字典</h4>
                    </legend>

                    <div class="form-group has-feedback">
                        <label class="col-sm-2 control-label">KEY</label>
                        <div class="col-sm-3">
                            <input class="form-control" name="key" type="text" value="{{ code.key }}"/>
                        </div>
                        <label class="col-sm-2 control-label">VALUE</label>
                        <div class="col-sm-3">
                            <input class="form-control" name="value" type="text" value="{{ code.value }}"/>
                        </div>

                    </div>

                    <div class="form-group has-feedback">
                        <label class="col-sm-2 control-label">父菜单</label>
                        <div class="col-sm-3">
                            <select class="form-control select2" name="parent">
                                <option value={{ code.parent.id }}> {{ code.parent.value }} </option>
                                <option value=""></option>
                                {% for parent in code_parent %}
                                    <option value={{ parent.id }}> {{ parent.value }} </option>
                                {% endfor %}
                            </select>
                        </div>
                        <label class="col-sm-2 control-label">描述信息</label>
                        <div class="col-sm-3">
                            <input class="form-control" id="desc" name="desc" type="text" value="{{ code.desc }}"/>
                        </div>
                    </div>

                </fieldset>
            </div>
            <div class="box-footer ">
                <div class="row span7 text-center ">
                    <button type="button" id="btnCancel" class="btn btn-default margin-right ">重置</button>
                    <button type="button" id="btnSave" class="btn btn-info margin-right ">保存</button>
                </div>
            </div>

        </form>
    </div>

{% endblock %}

{% block javascripts %}
    <script src="{% static 'plugins/select2/select2.full.min.js' %}"></script>
    <script type="text/javascript">
    $("#btnSave").click(function () {
        var data = $("#addForm").serialize();
        $.ajax({
            type: $("#addForm").attr('method'),
            url: "{% url 'cmdb:portal-code-update' %}",
            data: data,
            cache: false,
            success: function (msg) {
                if (msg.result) {
                    layer.alert('数据保存成功!', {icon: 1}, function (index) {
                        parent.layer.closeAll(); //关闭所有弹窗
                    });
                } else {
                    layer.alert(msg.error, {icon: 5});
                    //$('errorMessage').html(msg.message)
                }
                return;
            }
        });
    });


    /*点取消刷新新页面*/
    $("#btnCancel").click(function () {
        window.location.reload();

    });

    $(function () {
        //Initialize Select2 Elements
        $(".select2").select2();
    });

    </script>

{% endblock %}

打开sandboxMP/templates/cmdb/code.html,给修改绑定事件,在{% block javascripts %}中新建字典$("#btnCreate")的代码段后面添加如下内容:

//修改字典
function doUpdate(id) {
    layer.open({
        type: 2,
        title: '编辑',
        shadeClose: false,
        maxmin: true,
        area: ['800px', '400px'],
        content: ["{% url 'cmdb:portal-code-update' %}" + '?id=' + id, 'no'],
        end: function () {
            oDataTable.ajax.reload();
        }
    });
}

CTRL + S 保存并上传。

4.5 权限管理配置

请将下面URL数据添加到菜单管理,并授权给【系统管理】角色组:

7943b1fa2aa31ca1cf1d383278998949.png

运行项目测试字典修改功能。

5 字典删除功能实现

5.1 字典删除视图

回顾下用户管理、组织组织架构管理、菜单管理、角色管理这些功能中的删除视图的实现,尽管删除视图的实现在代码上已经算得上是很精简了,但是这些删除视图代码基本一致,所以也可以抽象出来写成自定义类。
打开sandboxMP/apps/custom.py,添加如下内容:

class SandboxDeleteView(LoginRequiredMixin, SandboxMultipleObjectMixin, View):

    def post(self, request):
        context = dict(result=False)
        queryset = self.get_queryset()
        if 'id' in request.POST and request.POST['id']:
            id_list = map(int, request.POST['id'].split(','))
            queryset.filter(id__in=id_list).delete()
            context['result'] = True
        else:
            raise AttributeError("Sandbox delete view %s must be called with id. "
                                 % self.__class__.__name__)
        return JsonResponse(context)

打开sandboxMP/apps/cmdb/views_code.py,添加删除视图:

from custom import SandboxDeleteView

class CodeDeleteView(SandboxDeleteView):
    model = Code

5.2 字典删除URL

打开sandboxMP/apps/cmdb/urls.py,添加新的URL:

urlpatterns = [
    '''原有内容省略'''
    path('portal/code/delete/', views_code.CodeDeleteView.as_view(), name='portal-code-delete'),
    ]

5.3 模板配置

给删除按钮绑定删除事件,打开sandboxMP/templates/cmdb/code.html,在{% block javascripts %}标签中修改字典doUpdate()代码段后面添加如下内容:

//checkbox全选
    $("#checkAll").on("click", function () {
        if ($(this).prop("checked") === true) {
            $("input[name='checkList']").prop("checked", $(this).prop("checked"));
            $('#example tbody tr').addClass('selected');
        } else {
            $("input[name='checkList']").prop("checked", false);
            $('#example tbody tr').removeClass('selected');
        }
    });

    //批量删除
    $("#btnDelete").click(function () {
        if ($("input[name='checkList']:checked").length == 0) {
            layer.msg("请选择要删除的记录");
            return;
        }

        var arrId = new Array();
        $("input[name='checkList']:checked").each(function () {
            //alert($(this).val());
            arrId.push($(this).val());
        });

        sId = arrId.join(',');

        layer.alert('确定删除吗?', {
            title: '提示'
            , icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
            , time: 0 //不自动关闭
            , btn: ['YES', 'NO']
            , yes: function (index) {
                layer.close(index);
                $.ajax({
                    type: "POST",
                    url: "{% url 'cmdb:portal-code-delete' %}",
                    data: {"id": sId, csrfmiddlewaretoken: '{{ csrf_token }}'},
                    cache: false,
                    success: function (msg) {
                        if (msg.result) {
                            layer.alert("操作成功", {icon: 1});
                            oDataTable.ajax.reload();
                        } else {
                            //alert(msg.message);
                            layer.alert("操作失败", {icon: 2});
                        }
                        return;
                    }
                });
            }
        });
    });

    //删除单个数据
    function doDelete(id) {
        layer.alert('确定删除吗?', {
            title: '提示'
            , icon: 3 //0:感叹号 1:对号 2:差号 3:问号 4:小锁 5:哭脸 6:笑脸
            , time: 0 //不自动关闭
            , btn: ['YES', 'NO']
            , yes: function (index) {
                layer.close(index);
                $.ajax({
                    type: "POST",
                    url: "{% url 'cmdb:portal-code-delete' %}",
                    data: {"id": id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                    cache: false,
                    success: function (msg) {
                        if (msg.result) {
                            layer.alert('删除成功', {icon: 1});
                            oDataTable.ajax.reload();
                        } else {
                            //alert(msg.message);
                            layer.alert('删除失败', {icon: 2});
                        }
                        return;
                    }
                });
            }
        });
    }

CTRL + S 保存并上传

5.4 权限管理配置

请将下面URL数据添加到菜单管理,并授权给【系统管理】角色组:

1e05fa68e394267d2493818ca3ce91e7.png

运行项目测试批量删除和单条删除功能。

至此,项目中已经完成新增、修改、列表、删除的自定义功能,这些类在项目中有相同需求时都可以直接继承使用。

作业:了解YAML语法格式,以及通过python来读取和写入YAML文件

最新最全文档,请关注我的知识星球: https:// t.zsxq.com/MBiqJi2 (微信中打开链接)
本节文档对应源码版本: https:// github.com/RobbieHan/sa ndboxMP/tree/v2.05
轻量级办公管理系统项目开源地址: https:// github.com/RobbieHan/gi standard
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值