Django 使用 DataTables 插件总结

发布于 2024-10-30 08:38:20 字数 13907 浏览 9 评论 0

Datatables 插件是一款方便简单的展示数据的列表插件,本文介绍了怎样在 Django 中使用它。文章中例子已上传至 github

基本使用

Datatables 插件是一款方便简单的展示数据的列表插件。关于基本使用, 官方网站 上的已介绍的很详细,这里我再稍微过一下。

js 配置。包含 jquery 和 datatables 的 js

<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script stc="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>

css 配置。包含 dataTables 的 css

<link href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet">

html。初始化表格

 <table id="example" class="display" style="width:100%">
    <thead>
      <tr>
        <th>Name</th>
        <th>Position</th>
        <th>Office</th>
        <th>Age</th>
        <th>Start date</th>
        <th>Salary</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Tiger Nixon</td>
        <td>System Architect</td>
        <td>Edinburgh</td>
        <td>61</td>
        <td>2011/04/25</td>
        <td>$320,800</td>
      </tr>
      <tr>
        <td>Garrett Winters</td>
        <td>Accountant</td>
        <td>Tokyo</td>
        <td>63</td>
        <td>2011/07/25</td>
        <td>$170,750</td>
      </tr>
      <tr>
        <td>Ashton Cox</td>
        <td>Junior Technical Author</td>
        <td>San Francisco</td>
        <td>66</td>
        <td>2009/01/12</td>
        <td>$86,000</td>
      </tr>
    </tbody>
  </table>

js 配置。是表格 dataTable 化

 <script type="text/javascript">
   $(document).ready(function() {
    $('#example').DataTable();
  } );
 </script>

与 django 结合使用

这里以一个展示用户姓名年龄的表格举例。假设数据库(数据库使用 Django 默认自带数据库) 中有表格 User,它的字段有 name、age 两项。

基本使用

基本使用的话,则是 django 作为后端,将要显示的数据传给 DataTables 进行展示。具体用法比较简单,DataTables 的官网也很详细了。(官网文档都是第一份资料)
不多说,直接上代码

def get_basic_tables(request):
  """
  创建基本的 DataTables 表格
  """
  user_list = []
  for user_info in User.objects.all():
    user_list.append({
      'name': user_info.name,
      'age': user_info.age
    })

  return render(request, 'example/basic_tables.html', {
    'users': user_list
  })

上面代码主要就是将数据取出并返回。

前端的展示代码如下:

<table id="basic-table" class="table table-hover" width="100%">
  <thead>
    <tr>
      <th>学号</th>
      <th>姓名</th>
      <th>年龄</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>
  $(document).ready(function () {
    $("#basic-table").DataTable({
      // 表下方页脚的类型,具体类别比较到,见[官网](    https://datatables.net/examples/basic_init/alt_pagination.html)    
      "pagingType": "simple_numbers",
      //启动搜索框
      searching: true,
      destroy : true,
      // 保存刷新时原表的状态
      stateSave: true,
      // 将显示的语言初始化为中文
      "language": {
        "lengthMenu": "选择每页 _MENU_ 展示 ",
        "zeroRecords": "未找到匹配结果--抱歉",
        "info": "当前显示第 _PAGE_ 页结果,共 _PAGES_ 页",
        "infoEmpty": "没有数据",
        "infoFiltered": "(获取 _MAX_ 项结果)",
        "paginate": {
          "first": "首页",
          "previous": "上一页",
          "next": "下一页",
          "last": "末页"
        }
      },
      // 此处重要,该 data 就是 dataTables 要展示的数据.users 即为后台传递过来的数据
      data: {{ users | safe }},
      columns: [
        {
          data: null,
          width: "1%",
          // 若想前端显示的不一样,则需要"render"函数
          'render': function (data, type, full, meta) {
            return meta.row + 1 + meta.settings._iDisplayStart;
          }
        },
        {
          data: "name",
          'render': function (data, type, full, meta) {
            return '<a class="text-warning" style="color:#007bff" title="年龄为'+ full.age +'">'+ data +'</a>';
          }
        },
        {data: 'age'}
      ]
    })
  });

可以看到 html 中只初始化了表头,表的内容则在 javascript 中控制。最终显示出来的数据行,第一列是对表格数据的排序。从代码中看出,当 data 对应的数据被置为 null 时,单元格中的内容将由"render"对应的函数返回值决定。第一列 datarender 函数中 meta.row 相当于表格中行的索引,默认是从0开始,故为了学号显示从 1 开始,进行了加 1 操作。

render 函数中的四个参数可谓是大有作用。 参数 data 刚好就是该函数上方“data”键对应的值的内容,比如第二列中的数据为‘data”键的值为 name,则 render 函数中 data 就是 name。而参数 full 相当于后端传递过来的 users 中的每个 user 的索引,这样某一个单元格的内容想与它所在行的其他单元格进行互动,则可用 full 参数来传递。表格中当使用鼠标移动到名字上时,会显示到该人名的年龄,这一功能就是使用了 full:

   {
     data: "name",
     'render': function (data, type, full, meta) {
       return '<a class="text-warning" style="color:#007bff" title="年龄为'+ full.age +'">'+ data +'</a>';
     }
   },

ajax 请求数据使用

基本操作

这种使用方法,则是前端发送 ajax 请求去后端获取数据,而不是一开始就有后端将数据传送到前端的。当数据再由后端传递回前端时,前端会自己进行处理,如分页等。下面例子是展示年龄为 22 周岁的人员表格

<table id="ajax-table" class="table table-hover" width="100%">
  <thead>
    <tr>
      <th>学号</th>
      <th>姓名</th>
      <th>年龄</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>

html 页面中依旧只是初始化了表头。

  $(document).ready(function () {
    //django post 请求需要加认证,不能忘了
    $.ajaxSetup({
      data: {csrfmiddlewaretoken: '{{ csrf_token }}' }
    });

    var table = $('#ajax-table').DataTable({
      "pagingType": "full_numbers",
      // 跟基本使用对比,已经没有 data 属性,而是多了"ajax"
      "ajax":{
        "processing": true,
        // ajax 请求的网址
        "url": "{% url 'example:request_ajax' %}",
        "type": 'POST',
        "data": {
          // 前端向后端传递的数据 age,比如只查询年龄在 22 岁的人员
          "age": 22
        },
        //
        "dataSrc": ""
      },
      // ajax 请求成功传递回来后数据的展示
      columns: [
         {
          data: null,
          width: "1%",
          // 若想前端显示的不一样,则需要"render"函数
          'render': function (data, type, full, meta) {
            return meta.row + 1 + meta.settings._iDisplayStart;
          }
        },
        {
          data: "name",
          'render': function (data, type, full, meta) {
            return '<a class="text-warning" style="color:#007bff" title="年龄为'+ full.age +'">'+ data +'</a>';
          }
        },
        {data: 'age'}
      ],
      "language": {
        "processing": "正在获取数据,请稍后...",
        "lengthMenu": "选择每页 _MENU_ 展示 ",
        "zeroRecords": "未找到匹配结果--抱歉",
        "info": "当前显示第 _PAGE_ 页结果,共 _PAGES_ 页, 共 _TOTAL_ 条记录",
        "infoEmpty": "没有数据",
        "infoFiltered": "(获取 _MAX_ 项结果)",
        "sLoadingRecords": "载入中...",
        "paginate": {
          "first": "首页",
          "previous": "上一页",
          "next": "下一页",
          "last": "末页"
        }
      }
    } );
  });

后端代码处理 ajax 请求:

def request_ajax(request):
  """
  处理 ajax 的例子中的 post 请求
  :param request:
  :return:
  """
  try:
    if request.method == "POST":
      # print(request.POST)
      #  获取到前端页面 ajax 传递过来的 age
      age = int(request.POST.get('age', 22))

      user_list = []
      for user_info in User.objects.filter(age=age):
        user_list.append({
          'name': user_info.name,
          'age': user_info.age
        })

      # 主要是将数据库查询到的数据转化为 json 形式返回给前端
      return HttpResponse(json.dumps(user_list), content_type="application/json")
    else:
      return HttpResponse(f'非法请求方式')
  except Exception as e:
    return HttpResponse(e.args)

相比来看跟基本使用没多少区别,只是多了一步 ajax 请求而已。

后端分页

当我们要往前端展示的数据量过大时,如果还是一股脑将数据全部扔给前端来处理,那么你会发现前端分页加载的性能很差,这时我们可以将分页操作放到后端来做。
其实将分页放到后端的意思就是对后台数据库中的数据进行部分请求。我们首先可以这样想:用户在前端页面查看表格时,他其实只关心这一页数据,他看不到其他页的数据。要看到其他页的数据,他必须得点击网页中的上一页或下一页按钮。

理解了这一点,我们是否可以这样做,即:“用户想看哪一页的数据,我就只去后台数据库查询这一页的数据。” 有了这样的理解,下来就是具体操作了。这个思路其实类似与 Python 语法中列表的切片功能,例如:

test_list = [1, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# 测试我们只需要这个列表中第 3 个到 6 个这 4 条数据,那么用列表的切片
little_list = test_list[2:6]

要查询到某一页展示的数据是哪些,必须知道这一页的数据的对应的数据起始位置和结束位置。
恰好在 DataTables 中,每次用户点击翻页(上一页或下一页)按钮时,前端都会向后端发送一次 ajax 请求。 而这次请求,前端则会将这一页的起始位置与结束位置传递到后端。

下面上代码:

<table id="basic-table" class="table table-hover" width="100%">
  <thead>
    <tr>
      <th>学号</th>
      <th>姓名</th>
      <th>年龄</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>
  $(document).ready(function () {
    //django post 请求需要加认证,不能忘了
    $.ajaxSetup({
      data: {csrfmiddlewaretoken: '{{ csrf_token }}' }
    });

    var table = $('#backend-table').DataTable({
      "pagingType": "full_numbers",
      // 跟基本使用对比,已经没有 data 属性,而是多了"ajax"
      searching: false,
      destroy: true,
      stateSave: true,
      // 此处为 ajax 向后端请求的网址
      sAjaxSource: "{% url 'example:request_backend' %}",
      "processing": false,
      "serverSide": true,
      "bPaginate" : true,
      "bInfo" : true, //是否显示页脚信息,DataTables 插件左下角显示记录数
      "sDom": "t<'row-fluid'<'span6'i><'span6'p>>",//定义表格的显示方式
      //服务器端,数据回调处理
      "fnServerData" : function(sSource, aoData, fnCallback) {
        $.ajax({
          "dataType" : 'json',
          // 此处用 post,推荐用 post 形式,get 也可以,但可能会遇到坑
          "type" : "post",
          "url" : sSource,
          "data" : aoData,
          "success" : function(resp){
            fnCallback(resp);
          }
        });
      },
      // ajax 请求成功传递回来后数据的展示
      columns: [
         {
          data: null,
          width: "1%",
          // 若想前端显示的不一样,则需要"render"函数
          'render': function (data, type, full, meta) {
            return meta.row + 1 + meta.settings._iDisplayStart;
          }
        },
        {
          data: "name",
          'render': function (data, type, full, meta) {
            return '<a class="text-warning" style="color:#007bff" title="年龄为'+ full.age +'">'+ data +'</a>';
          }
        },
        {data: 'age'}
      ],
      "language": {
        "processing": "正在获取数据,请稍后...",
        "lengthMenu": "选择每页 _MENU_ 展示 ",
        "zeroRecords": "未找到匹配结果--抱歉",
        "info": "当前显示第 _PAGE_ 页结果,共 _PAGES_ 页, 共 _TOTAL_ 条记录",
        "infoEmpty": "没有数据",
        "infoFiltered": "(获取 _MAX_ 项结果)",
        "sLoadingRecords": "载入中...",
        "paginate": {
          "first": "首页",
          "previous": "上一页",
          "next": "下一页",
          "last": "末页"
        }
      }
    } );
    // 每隔 5 秒刷新一次数据
    // setInterval(refresh, 5000);
  });
  function refresh() {
    var table = $('#backend-table').DataTable();
    table.ajax.reload(null, false); // 刷新表格数据,分页信息不会重置
  }

后端代码:

# 暂时跳过 csrf 的保护
@csrf_exempt
def request_backend(request):
  """
  处理后端分页例子中的 post 请求
  :param request:
  :return:
  """
  try:
    if request.method == "POST":
      # 获取翻页后该页要展示多少条数据。默认为 10;此时要是不清楚 dataTables 的 ajax 的 post 返回值
      # 可以打印一下看看 print(request.POST)
      page_length = int(request.POST.get('iDisplayLength', '10'))
      # 该字典将转化为 json 格式的数据返回给前端,字典中的 key 默认的名字不能变
      rest = {
        "iTotalRecords": page_length,  # 本次加载记录数量
      }
      # 获取传递过来的该页的起始位置,第一页的起始位置为 0.
      page_start = int(request.POST.get('iDisplayStart', '0'))
      # 该页的结束位置则就是"开始的值 + 该页的长度"
      page_end = page_start + page_length
      # 开始查询数据库,只请求这两个位置之间的数据

      users = User.objects.all()[page_start:page_end]
      total_length = User.objects.all().count()

      user_list = []
      for user_info in users:
        user_list.append({
          'name': user_info.name,
          'age': user_info.age
        })
      # print(start, ":", length, ":", draw)
      # 此时的 key 名字就是 aaData,不能变
      rest['aaData'] = user_list
      # 总记录数量
      rest['iTotalDisplayRecords'] = total_length
      return HttpResponse(json.dumps(rest), content_type="application/json")
    else:
      return HttpResponse(f'非法请求方式')
  except Exception as e:
    return HttpResponse(e.args)

如果我们想让前端实现定时刷新,可以用 js 的setInterval方法。具体如下:

function refresh() {
    var table = $('#backend-table').DataTable();
    table.ajax.reload(null, false); // 刷新表格数据,分页信息不会重置
}
// 每隔 5 秒刷新一次数据
setInterval(refresh, 5000);

最后

以上的还有好多的 dataTables 插件的方法没有使用到, 官网 有很多的使用事例,也非常详细,此处也只是把常用到的进行了归纳。这里多说一句,关于 ajax 请求后 DataTables 默认的查询会不起作用,但它会将查询框中的 text 通过 ajax 返回,需要自己在后端进行处理。
以上内容若有错误,请及时指正哈!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

xu362930323

文章 0 评论 0

缱倦旧时光

文章 0 评论 0

qq_eXruk9

文章 0 评论 0

遂心如意

文章 0 评论 0

guojiayue1

文章 0 评论 0

愿与i

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文