Django - 如何实现用户进行数据查询(表格)的功能?datatable - client processing

问题背景:

在笔者的练习项目中, 用户在登录之后, 需要查询当前系统已经录入了多少本书。 那么这就带来了以下问题:

  • 需要保存用户输入的查询条件
  • 后端需要处理用户输入的查询条件, 并返回相应的数据
  • 前端能以表格的形式展示数据
  • 前端能通过指定的数据项进行升序/降序排列
  • 前端能指定表格一页所展示多少行数据

这个应用场景, 几乎在所有的后端应用中都存在。 那么在本文中将要重点讲述怎么处理前端怎么通过表格展示后端的数据

Metronic 模版中的表格

Metronic中有很多的表格模板, 大家可以根据自己的需要选择其中一种就可以。 先看一下Metronic的表格的HTML实现。

<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
    <thead>
        <tr>
            <th>
                <label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
                    <input type="checkbox" class="group-checkable" data-set="#sample_1 .checkboxes" />
                    <span></span>
                </label>
            </th>
            <th> Username </th>
            <th> Email </th>
            <th> Status </th>
            <th> Joined </th>
            <th> Actions </th>
        </tr>
    </thead>
    <tbody>
        <tr class="odd gradeX">
            <td>
                <label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
                    <input type="checkbox" class="checkboxes" value="1" />
                    <span></span>
                </label>
            </td>
            <td> shuxer </td>
            <td>
                <a href="mailto:shuxer@gmail.com"> shuxer@gmail.com </a>
            </td>
            <td>
                <span class="label label-sm label-success"> Approved </span>
            </td>
            <td class="center"> 12 Jan 2012 </td>
            <td>
                <div class="btn-group">
                    <button class="btn btn-xs green dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false"> Actions
                        <i class="fa fa-angle-down"></i>
                    </button>
                    <ul class="dropdown-menu" role="menu">
                        <li>
                            <a href="javascript:;">
                                <i class="icon-docs"></i> New Post </a>
                        </li>
                        <li>
                            <a href="javascript:;">
                                <i class="icon-tag"></i> New Comment </a>
                        </li>
                        <li>
                            <a href="javascript:;">
                                <i class="icon-user"></i> New User </a>
                        </li>
                        <li class="divider"> </li>
                        <li>
                            <a href="javascript:;">
                                <i class="icon-flag"></i> Comments
                                <span class="badge badge-success">4</span>
                            </a>
                        </li>
                    </ul>
                </div>
            </td>
        </tr>

    </tbody>
</table>

根据以上代码, 比较好理解, <thead></thead>标签中的是表格标题栏。 图例中的标题栏,第一列是一个checkbox控件。 然后依次是Username/Email/Status/Joined/Actions。
其效果如下图所示:


表格效果

在<tbody></tbody>标签中的是表格内容栏。在图例中, 一共有25个记录。是在模版文件中一共有25个<tr></tr>,该标签定义HTML中的一行, 一个<tr></tr>表示一行。
在本文所给出的HTML实现中,为了缩减篇幅,特意只保留了一行。

在Metronic的模板示例文件中包含了以下内容

<script src="{% static '/global/scripts/datatable.js' %}" type="text/javascript"></script>
<script src="{% static '/global/plugins/datatables/datatables.min.js' %}" type="text/javascript"></script>
<script src="{% static '/global/plugins/datatables/plugins/bootstrap/datatables.bootstrap.js' %}"
            type="text/javascript"></script>

这说明Metronic使用的是datatable插件。 搜索了一下,datatable的官方网站在这里传送门。 官方介绍如下:

DataTables is a plug-in for the jQuery Javascript library. It is a highly flexible tool, based upon the foundations of progressive enhancement, and will add advanced interaction controls to any HTML table.

我们先不要管datatable的诸多功能特性。 目前面临的问题是, 我们怎么根据网站的实际内容, 生成前端表格所需要的内容,即<tr></tr>标签的内容。

使用Django模板生成表格内容

在笔者的练习项目中,需要展示书名/出版社/借阅次数/数量/作者/状态/ISBN这些信息。 那么对表格内容的处理如下, 使用django的模板语言
{% for item in items %}和{%s endfor %}, 就可以展示来自服务器的数据呢

<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
      <thead>
        <tr>
            <th>
                 <label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
                    <input type="checkbox" class="group-checkable" data-set="#id_booksTable .checkboxes"/>
                    <span></span>
                </label> 
            </th>
            <th> 书名</th>
            <th> 出版社</th>
            <th> 借阅次数</th>
            <th> 数量</th>
            <th> 作者</th>
            <th> 状态</th>
            <th> ISBN</th>
        </tr>
    </thead> 
    <tbody>
    </tbody>             
    <tbody>
        {% for item in items %}
        <tr class="odd gradeX">
            <td>
                <label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
                    <input type="checkbox" class="checkboxes" value="1"/>
                    <span></span>
                </label>
            </td>
            <td>{{ item.name }}</td>
            <td>{{ item.press }}</td>
            <td>{{ item.lendCount }}</td>
            <td>{{ item.acount }}</td>
            <td>{{ item.author }}</td>
            <td>{{ item.status }}</td>
            <td>{{ item.isbn   }}</td>
        </tr>
        {% endfor %}
    </tbody>

</table>
Django后台处理
def get(self, request):
    items = []
    testline = {}

    for i in range(100):
        testline['bookname'] = "Python Web开发测试驱动方法"
        testline['press'] = "人民邮电出版社"
        testline['lendCount'] = 4
        testline['amount'] = 5
        testline['author'] = "Harry J.W. Percival"
        testline["importTime"] = "2017/12/29"
        testline["status"] = "busy"
        testline["class"] = "IT"
        testline["isbn"] = 1234567            
        items.append(testline.copy())

    return render(request, 'p_books.html', {'items': items})

这里,我使用for循环生成来100个测试数据。让我们来看一下效果:


测试效果

数据是已经出来了, 但存在以下几个问题:

  • 第一行“No data available in table”
  • 100行数据一起显示出来,并没有根据一页显示20个记录的设置来显示

这说明我提出的办法并不是datatable插件所接受的。
那究竟该怎样做呢?

HTML实现
<table class="table table-striped table-bordered table-hover table-checkable order-column" id="sample_1">
      <thead>
        <tr>
            <th>
                 <label class="mt-checkbox mt-checkbox-single mt-checkbox-outline">
                    <input type="checkbox" class="group-checkable" data-set="#id_booksTable .checkboxes"/>
                    <span></span>
                </label> 
            </th>
            <th> 书名</th>
            <th> 出版社</th>
            <th> 借阅次数</th>
            <th> 数量</th>
            <th> 作者</th>
            <th> 状态</th>
            <th> ISBN</th>
        </tr>
    </thead> 
    <tbody>
    </tbody>             
</table>

注意, 这里我们并不需要写<tbody></tbody>这里面的内容,就让datatable插件帮我们处理吧

js实现
$(document).ready(function() {

    $("#sample_1").dataTable(
        { 
            searching : false,
            destroy : true,
            "ajax": {
                "url": "book/query/",
                "type": "get",
                "dataSrc":"",
            },
            columns: [
                { },
                { data: 'name' },
                { data: 'press' },
                { data: 'lendCount' },
                { data: 'acount' },
                { data: 'author' },
                { data: 'status' },
                { data: 'isbn' }

            ],
            'columnDefs': [{
                'targets': 0,
                'searchable': false,
                'orderable': false,
                'className': 'select-checkbox',
                'render': function (data, type, full, meta){
                return  '<label class="mt-checkbox mt-checkbox-single mt-checkbox-outline"><input type="checkbox" class="checkboxes" value="1"/><span></span></label>'
                }
            }],                

         }

    );                
});

在这里面需要注意的是,ajax里面需要说明选项dataSrc为"",这是因为我们在Django实现中以json array直接返回的数据。

Django 实现
def get(self,request):
    if request.method == "GET":

        objects = Book.objects.all()
   
        dic = [obj.as_dict() for obj in objects]

        return HttpResponse(json.dumps(dic), content_type="application/json")

as_dict的定义在book model中:

class Book(models.Model):
    name = models.CharField(max_length=48)
    author = models.CharField(max_length=24)
    isbn = models.IntegerField()
    status = models.CharField(max_length=12)
    press = models.CharField(max_length=48)
    acount = models.IntegerField()
    lendCount = models.IntegerField()

    def as_dict(self):
        return {
            "name": self.name,
            "author": self.author,
            "isbn" : self.isbn,
            "status" : self.status,
            "press" : self.press,
            "acount" : self.acount,
            "lendCount" : self.lendCount
        }

这里需要注意的是需要在对应的url book/query/中定义get方法,并以json格式返回数据。

在如此这般操作之后, 效果如下:


正常效果

可以看到数据已经从后台传到了前台。 并且datatable插件已经帮我们处理好了,分页,排序功能。

再思考

因为采取的是 client processing的处理方式, 这种方式需要服务器一次性把需要显示的数据全部发给前端。 那么这就有了以下几个问题:

  • 数据库太大的时候,比如有百万级别数据的时候, 一次性发送json数据的速度就有待考证。
  • 前端一次性获得数据之后,再分页的时候,都是上次浏览的时候缓存的数据处理的,那么在用户眼前的数据可能并不是服务器中实时的数据

怎么解决这两个问题, 就需要使用server processing的处理方式。 下次再讲

参考文献:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容