Bootstrap

Django入门教程——动态表格分页展示数据

第五章 动态表格分页展示数据

教学目的

  • 初步了解layui的使用
  • 理解table组件在layui中的应用
  • 了解数据添加、删除、编辑操作引入JavaScript框架的优势

layUI动态表格

表格组件 table 是 Layui 中使用率极高的一个组件,它以表格的承载方式对数据进行渲染、重载、排序、统计、分页等等一系列交互操作,并提供了丰富的 API 用于扩展,基本涵盖了日常业务所涉及的大部分需求。

表格标签

<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>

  • layui-hide类样式,表格标签不显示,表格中没有行列
  • 必须定义表格的id
  • lay-filter layui中表格事件对象标识

表格的渲染

在layui中通过table.render()函数实现表格数据的渲染,常用属性。

  • elem:表格对象ID
  • url:表格数据接口地址
  • method数据查询是通过post还是get
  • toolbar数据表格上部工具栏模板
  • defaultToolbar表格右上角工具图标
  • cols 表格列数组
  • limits数组,表格每页显示数据条数方案
  • limit默认每页显示记录数
  • page 是否开启分页
  • skin设置表格边框风格。可选值:grid|line|row|nob
  • even是否开启隔行背景。

模板定义

基本语法结构:

<script type="text/html" id="toolbar">
<!--html标签-->
</script>

常用的几个事件

  1. 表格事件

    基本语法:table.on('event(filter)', callback);

    • toolbar头部工具栏事件
    • tool单元格工具事件。可在该事件中实现行的更新与删除操作。
  2. 表格方法

    table.reload(id, options, deep) 表格完整重载。

创建动态表格实现数据展示

建立动态表格实现数据展示的模板

  1. 创建数据展示表格的通用模板页面base_list.html

    {%extends 'common/layout.html'%}
    {%block content%}
    <div class="layui-card-body">
        <!-- 查询操作区一 -->
        <div class="layui-collapse" style="margin-bottom: 10px;">
            <div class="layui-colla-item">
                <div class="layui-colla-title">查询</div>
                <div class="layui-colla-content layui-show">
                    {% block query%}{% endblock%}
                </div>
            </div>
        </div>
        <!-- 表格区域 -->
        <div class="layui-form layui-table-box">
            <table class="layui-hide" id="TableId" lay-filter="TableFilter"></table>
        </div>
    </div>
    {% block templet %}{% endblock %}
    {% endblock %}
    
    • 模板页面主要有两部分组成,查询区域和表格区域。
    • 新添加了templet模板块,用于动态表格的元素模板的存放。
  2. 修改employ_list.html模板页面,使用动态模板显示数据

    {%extends 'common/base_list.html'%}
    {% block templet %}
    <!-- 顶部工具栏模板 -->
    <script type="text/html" id="toolbar">
        <div class="layui-btn-container">
            <button class="layui-btn  layui-btn-sm" lay-event="add">
                <i class="layui-icon layui-icon-addition"></i>
                新增
            </button>
        </div>
    </script>
    <!-- 表格操作模板 -->
    <script type="text/html" id="TableBar">
        <div class="layui-clear-space">
            <a class="layui-btn layui-btn-xs " lay-event="edit">
                <i class="layui-icon layui-icon-edit"></i>
                编辑
            </a>
            <a class="layui-btn layui-btn-xs layui-bg-red" lay-event="delete">
                <i class="layui-icon layui-icon-delete"></i>
                删除
            </a>
        </div>
    </script>
    {%endblock%}
    {% block js %}
    <script>
    layui.use(function(){
        var $ = layui.jquery,
            form = layui.form,
            table = layui.table,
            layer = layui.layer;
        // 渲染表格
        table.render({
            elem: '#TableId',
            url: '/employee/list/',
            method:'post',
            toolbar: '#toolbar',
            defaultToolbar: ['filter', 'exports', 'print'],
            cols: [[
                {type: "checkbox", width: 50},
                {field: 'index', width: 80, title: '序号',align:'center', sort: true},
                {field: 'id', width: 80, title: 'ID',align:'center', sort: true,hide:true},
                {field: 'job_number', width: 150, title: '工号',align:'center',sort: true},
                {field: 'name', width: 150, title: '姓名',align:'center',sort: true},
                {field: 'depart', width: 150, title: '部门',align:'center',sort: true},
                {field: 'gender', width: 150, title: '性别',align:'center',sort: true},
                {field: 'birthday', width: 150, title: '出生日期',align:'center',sort: true},
                {field: 'phone', width: 150, title: '手机号',align:'center',sort: true},
                {field: 'hiredate', width: 150, title: '入职日期',align:'center',sort: true},
                {title: '操作', minWidth: 240, toolbar: '#TableBar', align: "center", fixed: 'right' }
            ]],
            limits: [10, 20, 50,100,200,500],
            limit: 10,
            page: true,
            skin: 'row',
            even: true,
        });
    })
    </script>
    {%endblock%}
    
  3. 运行可以看见基本界面,但是出现数据403错误原因是我们没有提供数据接口。

建立数据接口

  1. 查看页面https://layui.dev/static/json/2/table/demo1.json,可以看见表格数据的基本JSON格式。

  2. 在common文件夹中创建res_json_data.py文件存放JSON数据通用response函数

    # JSON 数据提交通用方法
    
    from django.http import JsonResponse
    
    def success_api(msg: str = "成功"):
        """ 成功响应 默认值”成功“ """
        res = {
            'msg': msg,
            'success': True,
        }
        return JsonResponse(res, safe=False)
    
    
    def fail_api(msg: str = "失败"):
        """ 失败响应 默认值“失败” """
        res = {
            'msg': msg,
            'success': False,
        }
        return JsonResponse(res, safe=False)
    
    
    def table_api(msg: str = "success", count=0, data=None, limit=10):
        """ 动态表格渲染响应 """
        res = {
            'msg': msg,
            'code': 0,
            'data': data,
            'count': count,
            'limit': limit
        }
        return JsonResponse(res, safe=False,json_dumps_params={'ensure_ascii': False})
    
    # 返回单页数据
    def json_api(code=200,msg="success",data=None):
        # 包括200(成功)、201(创建)、400(错误请求)、401(未授权)、403(禁止)、404(未找到)、500(服务器错误)
        res={
            "code": code,
            "message": msg,
            "data": data
        }
        return JsonResponse(res, safe=False, json_dumps_params={'ensure_ascii': False})
    
    # 返回多页数据
    def json_list_api(code=200,msg="success",data=None,total=0,limit=10, page=1):
        res={
            "code": code,
            "message": msg,
            "data": {
                'total': total,
                'limit': limit,
                'page': page,
                'data': data
            }
        }
        return JsonResponse(res, safe=False,json_dumps_params={'ensure_ascii': False})
    
  3. 改造视图函数

    # 员工数据表格
    def employee_list(request):
        print(request.method,"==请求方式")
        if request.method == "GET":
            return render(request,"archives/employee_list.html")
    

    出现错误:CSRF token missing or incorrect,错误原因是ajax的post提交没有进行CSRF验证。

  4. 解决ajax提交不进行CSRF验证

    from django.views.decorators.csrf import csrf_exempt
    @csrf_exempt
    def  viewfun(request)
    
    1. 导入csrf_exempt
    2. 在ajax请求的视图函数上面加上@csrf_exempt注解(装饰器)。
  5. 改造视图函数,对POST请求返回表格需要的JSON格式数据。

    # 员工数据表格
    @csrf_exempt
    def employee_list(request):
        print(request.method,"==请求方式")
        if request.method == "GET":
            return render(request,"archives/employee_list.html")
        
        # post请求返回JSON
        data_obj = Employee.objects.all()
        data_list=[]
        for row in data_obj:
            data_list.append({
                "id":row.id,
                "name":row.name,
                "job_number":row.job_number,
                "phone":row.phone,
            })
        return res_json_data.table_api(data=data_list)
    

    分析这个程序在效率方面,主要是对JSON字典的创建,比较麻烦。

  6. 优化JSON字典的创建

    Model._meta.fields :返回模型字段对象列表

    # post请求返回JSON
    data_obj = Employee.objects.all()
    row_obj = data_obj[0]
    print(row_obj._meta.fields)
    field_names = [field.name for field in row_obj._meta.fields]
    print("==字段列表:",field_names)
    
    • ⑤行:采用列表推导式,生成一个列表
  7. 改造视图函数

    # post请求返回JSON
    data_obj = Employee.objects.all()
    data_list=[]
    for row in data_obj:
        item_dict = {}
        field_names = [field.name for field in row._meta.fields]
        for field in field_names:
            item_dict[field] = getattr(row,field)
        data_list.append(item_dict)
    return res_json_data.table_api(data=data_list)
    

    getattr(row,field):内置函数,等同于row.field

  8. 解决返回对象的错误

    for field in field_names:
        if field == "depart":
            item_dict[field] = row.depart.name
        else:
            item_dict[field] = getattr(row,field)
    
  9. 解决性别显示错误问题

    for field in field_names:
        if field == "depart":
            item_dict[field] = row.depart.name
        elif field == "gender":
            item_dict[field] = row.get_gender_display()
        else:
            item_dict[field] = getattr(row,field)
    

数据分页和排序

  1. 批量向人员表格中添加数据。

    def index(request):
        for i in range(100):
            new_data ={
                'name':f"员工{i}",
                'job_number':f"RS0{i}",
                'gender':1,
                'birthday':'2018-08-11',
                'hiredate':datetime.now(),
                'depart':Department.objects.first(),
            }
            # Employee.objects.create(**new_data)
            employee =Employee(**new_data)
            employee.save()
        return HttpResponse("<h1 style='color:red'>我的第一个Django视图</h1>")
    
    • 12行:用到了**对字典进行解包
    • 11行:可以通过create向数据库中插入元素
    • 12行,13行:通过创建一个模型对象,然后调用模型对象的save()方法保存到数据库中。注意:如果这里对象是存在的对象,save()方法将执行更新update操作,否则执行insert操作。
  2. 排序和分页的相关语法。

    排序:

    • 升序:实体模型.objects.all().order_by("排序字段1","排序字段2")
    • 降序:实体模型.objects.all().order_by("-排序字段")

    分页:

    Paginator 类用于对列表进行分页,它允许你将对象列表分成固定长度的页。

    语法 : Paginator(列表对象,每页数量)

    返回当前页数据 : page = paginator.page(页码)

  3. 改造视图函数实现排序和分页

    from django.core.paginator import Paginator
    page = request.POST.get('page', 1)
        limit = request.POST.get('limit', 10)
        data_obj = Employee.objects.all()
        data_page = Paginator(data_obj, limit).page(page)
        data_list=[]
        for row in data_page:
            item_dict = {}
            field_names = [field.name for field in row._meta.fields]
            ……
        return res_json_data.table_api(data=data_list,count=len(data_obj))
    
  4. 改造新增操作

    // 工具栏事件
    table.on('toolbar(TableFilter)', function (obj) {
        if (obj.event === 'add') {
            window.location.href = '/employee/add/';
        }
    });
    

    基本语法:table.on('event(filter)', callback);

    • toolbar头部工具栏事件
    • tool单元格工具事件。可在该事件中实现行的更新与删除操作。
  5. 改造数据排序

    data_obj = Employee.objects.all().order_by('-id')
    
  6. 改造编辑操作

    // 单元格事件
    table.on('tool(TableFilter)', function (obj) {
        var data = obj.data; //获得当前行数据
        if (obj.event === 'edit') {
            window.location.href = '/employee/edit/'+data.id+'/';
        }
    });
    
  7. 删除员工操作

    // 单元格事件
    table.on('tool(TableFilter)', function (obj) {
        var data = obj.data; //获得当前行数据
        if (obj.event === 'edit') {
            window.location.href = '/employee/edit/'+data.id+'/';
        }
        else if (obj.event === 'delete') {
            layer.confirm('确认删除吗?', {
                btn: ['确定', '取消'] //按钮
                }, function(index){
                    console.log('点击了确定');
                    layer.close(index);
                    window.location.href = '/employee/delete/'+data.id+'/';
                }
            );
        }
    });
    
    # 员工删除
    def employee_del(request,nid):
        Employee.objects.filter(id=nid).delete()
        return redirect("/employee/list/")
    

数据查询操作

查询语句基本语法

基本语法
  1. filter(**kwargs)

​ 返回一个新的 QuerySet,包含的对象满足给定查询参数。

  1. exclude(**kwargs)

​ 返回一个新的 QuerySet,包含的对象不满足给定查询参数。

表示并且关系查询
# 方法一:
UserInfo.objects.filter(name="张晓红",age=39).all()

# 方法二:
data_dict ={"name":"张晓红","age":39}
UserInfo.objects.filter(**data_dict).all()
表示或者关系查询
from django.db.models import Q
# 假设我们有一个 Employee 模型,部门和头衔都是外键,你可以直接使用外键字段
employees = Employee.objects.filter(
    Q(department__name='研发部') | Q(department__name='市场部'),
    Q(title__name='工程师') | Q(title__name='经理')
)

我们使用了 Q 对象来构建两个查询条件,每个条件都包含一个逻辑或操作。第一个查询条件检查 department 字段是否等于“研发部”或“市场部”,第二个查询条件检查 title 字段是否等于“工程师”或“经理”。

查询比较运算符
数字类型查询比较
  • 等于:data_list = UserInfo.objects.filter(age=26)
  • 大于:data_list = UserInfo.objects.filter(age__gt=26)
  • 大于等于:data_list = UserInfo.objects.filter(age__gte=26)
  • 小于:data_list = UserInfo.objects.filter(age__lt=28)
  • 小于等于:data_list = UserInfo.objects.filter(age__lte=28)
  • 不等于:data_list = UserInfo.objects.exclude(age=28)
  • 在列表中 :order_obj = Order.objects.filter(id__in=orderID_list).order_by('-update_time')
字符串比较
  • 等于:data_list = UserInfo.objects.filter(name="张晓红")
  • 不等于:data_list = UserInfo.objects.filter(name__ne="张晓红")
  • 以XX开头:data_list = UserInfo.objects.filter(name__startswith="刘")
  • 以XX结尾:data_list = UserInfo.objects.filter(name__endswith="红")
  • 包含XX:data_list = UserInfo.objects.filter(name__contains="小")
查询语句举例
  1. 查询所有性别为女的员工

    def index(request):
        data_obj = Employee.objects.filter(gender=2).all()
        res_list = [item.name for item in data_obj]
        print(res_list)
        return HttpResponse("<h1 style='color:red'>我的第一个Django视图</h1>")
    
  2. 查询销售部的所有员工

    data_obj = Employee.objects.filter(depart__name='销售部').all()
    
    data_obj = Employee.objects.filter(depart_id=5).all()
    
  3. 查询2018-1-1日之前出生的员工

    data_obj = Employee.objects.filter(birthday__lt="2018-1-1").all()
    
  4. 查询2018-1-1日之前出生的女员工

    data_obj = Employee.objects.filter(birthday__lt="2018-1-1",gender=2).all()
    
  5. 查询姓名中包含李的员工

    data_obj = Employee.objects.filter(name__contains="李").all()
    

查询操作改造

  1. 创建员工信息查询表单类

    class employee_seacher_form(forms.ModelForm):
        class Meta:
            model = Employee
            fields = ['name','gender','depart','job_number','phone']
        
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
            for name,field in self.fields.items():
                field.widget.attrs={"class":"layui-input","placeholder":"输入"+field.label}
                field.required = False
    

    field.required = False 设置所有字段都不是必须输入字段

  2. 改造视图函数

    # 员工数据表格
    @csrf_exempt
    def employee_list(request):
        print(request.method,"==请求方式")
        if request.method == "GET":
            init_data ={"gender":0}
            form =employee_form.employee_seacher_form(initial=init_data)
            return render(request,"archives/employee_list.html",{"form":form})
    
  3. 改造模板页面

    {% block query%}
    <form class="layui-form" novalidate>
        <div class="layui-form-item">
            {% for field in form %}
            <div class="layui-inline">
                <label class="layui-form-label w-auto">{{ field.label }}: </label>
                <div class="layui-input-inline">
                    {{ field }}
                </div>
            </div>
            {% endfor %}
            <div class="layui-inline" style="float: right;">
                <div class="layui-input-inline" style="width: auto;">
                    <button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="data-search-btn"
                        id="search"><i class="layui-icon layui-icon-search"></i>查 询
                    </button>
                    <button class="layui-btn layui-btn-primary"><i class="layui-icon layui-icon-refresh"></i> 重
                        置
                    </button>
                </div>
            </div>
        </div>
    </form>
    {%endblock%}
    
  4. 查询事件的处理

    // 监听搜索操作
    form.on('submit(data-search-btn)', function (data) {
        console.log(data.field)
        var result = JSON.stringify(data.field);
        //执行搜索重载
        table.reload('TableId', {
            page: {
                curr: 1
            },
            where: {
                searchParams: result
            }
        }, 'data');
        return false;
    });
    
    • JSON.stringify():将 JavaScript 对象或值转换为 JSON 格式的字符串。
    • data.field :layui表单方法,可以获取表单中所有控件的值
  5. 查询函数的改造

    import json
     # post请求返回JSON
    page = request.POST.get('page', 1)
    limit = request.POST.get('limit', 10)
    searchParams = request.POST.get('searchParams', None)
    if searchParams:
        searchParams =json.loads(searchParams)
        fileld_names = list(searchParams.keys())
        fileld_vaules = list(searchParams.values())
        filter_dict = dict()
        for i in range(len(fileld_vaules)):
            if fileld_vaules[i]:
                item = fileld_names[i]+"__contains"
                filter_dict[item] = fileld_vaules[i]
        print(filter_dict,"==查询参数")
        data_obj = Employee.objects.filter(**filter_dict).order_by('-id')
    else:
        data_obj = Employee.objects.all().order_by('-id')
    data_page = Paginator(data_obj, limit).page(page)
    

    json.loads() 是 Python 的标准库模块 json 中的一个函数,用于将 JSON 格式的字符串解码成 Python 字典对象。

  6. 修改部门查询错误问题

    for i in range(len(fileld_vaules)):
        if fileld_vaules[i]:
            if fileld_names[i] == "depart":
                item = fileld_names[i]+"_id"
            else:
                item = fileld_names[i]+"__contains"
            filter_dict[item] = fileld_vaules[i]
    

优化编辑删除等操作

弹出层组件

弹出层组件 layer 是 Layui 最古老的组件,也是使用覆盖面最广泛的代表性组件。 layer 集众多弹层功能为一体,灵活而多样,是许多开发者的网页弹出层的首选交互方案,在各类业务场景都能发挥重要作用。

layer.open({
    title: ['编辑', 'font-size:18px;font-weight:bold;'],
    type: 2,
    shade: 0.3,
    maxmin:true,
    shadeClose: true,
    area: ['60%', '400px'],
    content: url+"?id="+data.id,
});

上面函数可以弹出一个iframe页面层。

  • title:弹出层的标题
  • type:弹层类型,2,为 iframe 内联框架层
  • shade:弹层的遮罩。shade: 0.3 设置遮罩深色背景的透明度;shade: [0.3, '#FFF'] 设置遮罩透明度和颜色值;shade: 0 不显示遮罩
  • maxmin:是否开启标题栏的最大化和最小化图标。
  • shadeClose:是否点击遮罩时关闭弹层。当遮罩存在时有效。
  • area:设置弹层的宽高
  • content:弹层内容。若 type: 2(iframe 层)则为url地址

具体操作

  1. 改造模板编辑函数

    if (obj.event === 'edit') {
        layer.open({
            title: ['编辑', 'font-size:18px;font-weight:bold;'],
            type: 2,
            shade: 0.3,
            maxmin:true,
            shadeClose: true,
            area: ['60%', '400px'],
            content: '/employee/edit/'+data.id+'/',
        });
    }
    
  2. 解决拒绝链接请求的错误

    settings.py中添加

    X_FRAME_OPTIONS = 'SAMEORIGIN'  # 设置Frame-Options为SAMEORIGIN,否则无法在iframe中加载页面
    

    X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 frame , iframe 或者 object 中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。

    X-Frame-Options有三个可能的值:

    • deny 表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
    • sameorigin 表示该页面可以在相同域名页面的 frame 中展示。
    • allow-from uri(http://......) 表示该页面可以在指定来源的 frame 中展示。
  3. 创建对话框公共模板页面layout_dlg.html

    {% load static %}
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>layui</title>
        <meta name="renderer" content="webkit">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
        <link href="{%static 'layui/css/layui.css'%}" rel="stylesheet" media="all">
        {% block css %}{% endblock %}
        <style>
            /* 必须字段 */
            .label-required-next:after {
                top: 6px;
                right: 5px;
                color: red;
                content: '*';
                position: absolute;
                margin-left: 4px;
                font-weight: 700;
                line-height: 1.8em;
            }
            /* 只读文本框 */
            .readonly-input {
                background-color: #f2f2f2;
                border: 1px solid #d2d2d2;
            }
        </style>
    </head>
    <body style="background-color: #FFF;">
    <div class="layui-form">
        {% block content %}{% endblock %}
    </div>
    <script src="{%static 'layui/layui.js'%}"></script>
    {% block js %}{% endblock %}
    </body>
    
  4. 创建两行编辑模板

    {%extends 'common/layout_dlg.html'%}
    {%block content%}
    <div class="layui-card-body">
        <form class="layui-form"  method="post" novalidate>
            <div class="layui-form-item">
                {% csrf_token %}
                {% for field in form %}
                    <div class="layui-inline">
                        {% if field.field.required %}
                            <label class="layui-form-label label-required-next">{{ field.label }}:</label>
                        {% else %}
                            <label class="layui-form-label">{{ field.label }}:</label>
                        {% endif %}
                            <div class="layui-input-inline layui-input-wrap">
                                {{ field }}
                                <span style="color: red">{{ field.errors.0 }}</span>
                            </div>
                    </div>
                {% endfor %}
             </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button type="submit" class="layui-btn" lay-submit>确认提交</button>
                </div>
            </div>
        </form>
    </div>
    {% endblock %}
    {%block js%}
    {% block extendsjs %}{% endblock %}
    {% for msg in messages %}
    <script>
        layui.use(['layer'], function () {
            var iconValue = "{{msg.tags}}" == 'success' ? 1 : 2;
            layer.msg(
                '{{msg}}',
                { icon: iconValue, time: 2000 },
                function (){
                    if( "{{msg.tags}}" == 'success' ){
                        parent.layer.close(parent.layer.getFrameIndex(window.name)); //关闭当前页
                        parent.layui.table.reload('TableId');//重新加载父窗口表格
                    }
                }
            )
        })
    </script>
    {% endfor %}
    {% endblock %}
    
    • layui-row layui-col-xs6是Layui 栅格系统,采用业界比较常用的容器横向 12 等分规则
    • layer.getFrameIndex(window.name);在 iframe 页中获取弹层索引
    • layer.close(index, callback);关闭弹层。参数 index 打开弹层时返回的唯一索引;参数 callback 关闭弹层后的回调函数
  5. 改造编辑视图函数,进行提醒和关闭对话框

    from django.contrib import messages
    ……
    if form.is_valid():
        form.save()
        messages.success(request, '编辑成功')
        return render(request,"archives/employee_add.html",{"form":form})
    ……
    

    返回错误信息

    • 方法一 : 通过form对象附加到下相应的字段上

      if form.is_valid():
          # 获取输入的验证码,并从form中移除
          input_captcha = form.cleaned_data.pop('captcha')
          captcha_code = request.session.get('code','')
          request.session["code"] = None  # 验证码验证后,移除session中的验证码
          if captcha_code.upper() != input_captcha.upper():
              form.add_error("captcha","验证码错误")
              return render(request,'login/login.html',{"form":form})
      
    • 方法二 : 通过Message进行传递

      #views
      from django.contrib import messages
      messages.warning(request, '用户名或密码错误')
      
      #html
      {% for msg in messages %}
         layer.alert('{{ msg}}');
      {% endfor %}
      

    django.contrib.messages详解

    后端传递消息的方法:

    • messages.debug(request, '消息1')将在生产部署中被忽略(或删除)的与开发相关的消息
    • messages.info(request, '消息2')为用户提供信息消息
    • messages.success(request, '消息3')行为成功消息
    • messages.warning(request, '消息4')失败并没有发生,但可能即将发生
    • messages.error(request, '消息5')一个操作没有成功,或者发生了其他一些失败

    前端将通过messages列表对象传递消息

    • 可以通过message.tags拿到每个消息
    • 通过message拿到具体的消息
  6. 改造列表数据生成记录编号

    ……
    data_list=[]
    count = (int(page) - 1) * int(limit)
    for row in data_page:
        count += 1
        item_dict = {}
        field_names = [field.name for field in row._meta.fields]
        ……
    
  7. 改造查询选择框选择后自动执行查询

    1. 修改查询表单

      class employee_seacher_form(forms.ModelForm):
          class Meta:
              model = Employee
              fields = ['name','gender','depart','job_number','phone']
              widgets = {
                  "gender":forms.Select(attrs={"lay-filter":"select-filter"}),
                  "depart":forms.Select(attrs={"lay-filter":"select-filter"}),
              }
          
          def __init__(self,*args,**kwargs):
              super().__init__(*args,**kwargs)
              for name,field in self.fields.items():
                  field.widget.attrs.update({"class":"layui-input","placeholder":"输入"+field.label})
      
      • 通过模式表单的widgets属性给选择框添加lay-filter属性
      • 改造初始化函数属性采用update方法,避免覆盖。
    2. 添加选择框事件

      //监听选择框搜索事件
      form.on('select(select-filter)', function(data){
          $("#search").click()
      });
      

      通过Jquery方法调用查询按钮的单击事件

      layui选择框事件:form.on('select(filter)', callback);

      • select 为选择框事件固定名称
      • filter 为选择框元素对应的 lay-filter 属性值
;