datatables折腾日志

https://github.com/ygidtu/Flask-peewee-datatables上我挂了一个demo

datatables:官网【https://datatables.net/】

datatables折腾日志_第1张图片
datatables做用展示

如上图所示,datatables是一个非常好用的js库,方便在网页中展示各种结果


一、安装

Download:https://datatables.net/download/index
我常用两种:

  • 下载到本地以static文件的方式引入,但是,除了默认主题,其他的主题会存在图片定位不到的问题
  • CDN

二、基础用法

官方最简单的教学:https://datatables.net/examples/basic_init/zero_configuration.html
什么都不加,直接在HTML页面中生成一个table,

 
id chrom start end eid tissue order predict

然后直接通过JQuery调用DataTable即可,直接使用

$(document).ready(function() {
    $('#tfbs').DataTable( );
} );

当然,各种细节也可以自行调整,具体请自行查询官方文档


三、ajax

当然,这种成熟的JS库当然支持ajax的用法,它需要有特定的ajax格式
所需要的特定的json格式,格式如下:这只是非常基本的格式之一

{
  "draw": 1,
  "recordsTotal": 57,
  "recordsFiltered": 57,
  "data": [
    [
      "Airi",
      "Satou",
      "Accountant",
      "Tokyo",
      "28th Nov 08",
      "$162,700"
    ]
  }

ajax的JavaScript脚本如下:

$(document).ready(function() {
    $('#example').DataTable( {
        "processing": true,
        "serverSide": true,
        "ajax": {
            "url": "scripts/jsonp.php",
            "dataType": "jsonp"
        }
    } );
} );

三、Flask的配合问题

现成的Flask的API支持flask-rest、flask-restful和flask-peewee都无法修正为datatables的格式,因此,还是得自己动手丰衣足食
为了能够ajax的交互操作,必须搞清楚datatables是通过何种方式向服务器传递参数的

  • 最基础的,datatables会通过在url后附加各种参数来完成数据传递
  • 可以自己指定向api借口post数据

1. 拆分url参数

以最开始的例子为例,我开了一个/api/enhancer的接口,默认datatables会通过如下的url获取数据

GET /api/enhancer/?draw=17&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=chrom&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=start&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=end&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=true&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=eid&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=true&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=tissue&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=true&columns%5B5%5D%5Borderable%5D=true&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B6%5D%5Bdata%5D=order&columns%5B6%5D%5Bname%5D=&columns%5B6%5D%5Bsearchable%5D=true&columns%5B6%5D%5Borderable%5D=true&columns%5B6%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B6%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B7%5D%5Bdata%5D=myself&columns%5B7%5D%5Bname%5D=&columns%5B7%5D%5Bsearchable%5D=true&columns%5B7%5D%5Borderable%5D=true&columns%5B7%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B7%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=5&order%5B0%5D%5Bdir%5D=desc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1510720968919

返回的数据data类,如下:


datatables折腾日志_第2张图片
返回的数据格式

目前,已经拆分出几个参数

  • draw:页数,所需第几页的数据
  • %5B、%5D:
    • 根据Stack OverFlow所讲,分别代表[, ]
  • columns%5B0%5D%5Bdata%5D=id:
    • 原义为columns[0][data]=id,经过cprint(get_parameter("columns[0][data]"), 'red')证实,确实如此。这一大段,表明我的表格的第一列使用的是json中key值为id的这一项的数据,
  • columns%5B0%5D%5Bname%5D=:
    • columns[0][name]=,这一项不是何意,估计是可以该列名
  • columns%5B0%5D%5Bsearchable%5D=true:
    • columns[0][searchable]=true,能够查询
  • columns%5B0%5D%5Borderable%5D=true:
    • columns[0][orderable]=true,该列能够排序
  • columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=:
    • columns[0][search][value]= ,按照什么数据来查询该列
  • columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false:
    • columns[0][search][regex]=false,该列是否通过正则来匹配
  • 然后重复,因此,通过这些调整就将各行各列的所有数据都调整好了

2. 参数的详细用法

首先,将数据只调整到一列,开始解析各个参数的详细用法

order

通过调整order调整到这样一条url

api/enhancer/?draw=1&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1510727303124

order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc:
order[column]=0&order[0][dir]=asc或者dir=desc

  • draw:第几次向该api发出请求
  • order:column指定第几列调整顺序,order[0][dir=asc]或者order[0][dir=desc]指定排序方式
  • start=0&length=10,length 代表每一页上有多少数据,start是这一页最开始的那一条是第几条

search

GET /api/enhancer/?draw=13&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=chrom&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=1&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=chr1&search%5Bregex%5D=false&_=1510727522537 HTTP/1.1

不出所料

  • search%5Bvalue%5D=chr1
    • search[value]=chr1

3. Python配置

通过以上的参数,相信参数的事情就解析清楚了,那么就可以自行动手,设计API来处理这些数据了,我使用的是peewee来处理底层的数据库,因此,写了一个类来处理这个问题

用法已经放在github库上了:https://github.com/ygidtu/Flask-peewee-datatables

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
u"""
serverside的datatables配合flask和peewee使用的方法
"""
import re
from collections import OrderedDict
from flask import request
from peewee import BooleanField
from peewee import IntegerField
from peewee import FloatField


# flask配合datatables使用的类
class WorkWithDataTables(object):
    u"""
    2017.11.15
    开始设计类,想办法自行处理dtatables返回的参数
    """

    def __init__(self, table, columns=None, join=None):
        u"""
        初始化
        :param columns: 列表,传入查询的columns, 如果不指定columns,默认使用表中所有的列
        :param join: 列表,传入查询的如果需要join表,可以通过这个join指定实现,目前只能join一个表
        """
        # 构建
        if columns is None:
            columns = list(table._meta.fields.values())

            if join:
                columns += list(join._meta.fields.values())

        start = 0
        tem = []
        for i in columns:
            tem.append([start, i])
            start += 1
        self.columns = OrderedDict(tem)
        self.table = table
        self.join = join

    @staticmethod
    def get_parameter(label, default=None):
        u"""
          :param label: url中指定的参数是
          :param default: 如果url中没有这个参数,返回的默认值,一般为None
          :return:返回strip之后的参数
         """
        tem = request.args.get(label)

        if tem is None:
            return default
        return tem.strip()

    def _searchable_(self, index):
        u"""
        datatables可以通过JavaScript的设置指定某些列可以被查询,某些不可以,
        于是变有了这个函数,只有能够被查询的列,我才会查询
        """
        if self.get_parameter("columns[%d][searchable]" % index) == "true":
            return True
        else:
            return False

    def _set_order_(self):
        u"""
        根据datatables提交的参数,判定查询结果的排序方式
        :return: 返回特定排序方式的peewee Expression语句
        """
        # 指定排序方式
        order = self.get_parameter("order[0][column]")
        order_by = None

        if order:
            order = int(order)
            order_by = self.columns.get(order)
            order_dir = self.get_parameter("order[0][dir]")

            if order_by and order_dir == "desc":
                order_by = order_by.desc()

        return order_by

    def _set_search_(self):
        u"""
        根据url参数,设定查询的语句
        :return:peewee的查询Expression语句
        """
        # 指定查询的值是多少
        search = self.get_parameter("search[value]")

        # 判断一下输入的是否是boolean值,true、false、yes、no均可,不分大小写
        if search is not None:
            if re.search(r"^(true|yes)$", search, re.I):
                search = True
            elif re.search(r"^(false|no)$", search, re.I):
                search = False
            else:
                search = search
        #指定查询的peewee Expression语句
        search_condition = None
        if search:    #如果开启search需求
            for i in self.columns:    # 遍历所有的列,然后分别根据特定情形指定查询语句,目前仅支持bool、float、int
                if self._searchable_(i):
                    # 如果遇到布尔型,需要独特的处理方法
                    if isinstance(self.columns[i], BooleanField) and\
                            not isinstance(search, bool):
                        if search_condition is None:
                            search_condition = (self.columns[i] == None)
                        else:
                            search_condition = (
                                self.columns[i] == None) | search_condition
                        continue

                    # if this column is integer type
                    if isinstance(self.columns[i], IntegerField):
                        try:
                            search = int(search)
                            if search_condition is None:
                                search_condition = (self.columns[i] == search)
                            else:
                                search_condition = (
                                    self.columns[i] == search
                                ) | search_condition
                        except:
                            continue
                        continue

                    # if this column is float type
                    if isinstance(self.columns[i], FloatField):
                        try:
                            search = float(search)
                            if search_condition is None:
                                search_condition = (self.columns[i] == search)
                            else:
                                search_condition = (
                                    self.columns[i] == search
                                ) | search_condition
                        except:
                            continue
                        continue

                    # 如果符合要求就不用管了,正常处理即可
                    if search_condition is None:
                        search_condition = (
                            self.columns[i].regexp(str(search) + ".*"))
                    else:
                        search_condition = (self.columns[i].regexp(
                            str(search) + ".*")) | search_condition
        return search_condition

    def query(self, condition=None, search=True, order=None, **kwargs):
        u"""
        查询语句
        :param condition: 外部指定的特定查询要求,peewee Expression
        :param search: boolean值,在后期是否能支持datatables的查询框输入值查询
        :param order: 指定初始化的顺序,如果不设定,就使用默认顺序
        :return: 
        """
        # 获取参数
        page = int(self.get_parameter("start", 1)) / 10 + 1
        per_page = int(self.get_parameter("length", 10))
        draw = int(self.get_parameter("draw", 1))

        querys = self.table.select(*self.columns.values())

        if order:
            querys = querys.order_by(order)

        # 指定非第一次的查询,才会通过这个排序方式排序
        if draw > 1:
            order = self._set_order_()
            querys = querys.order_by(order)

        # 如果有外链表,就外链
        if self.join:
            querys = querys.join(self.join)

        # 如果有查询条件或者搜索条件,就分别指定不同的条件
        if condition and search:
            search_condition = self._set_search_()

            if search_condition:
                condition = condition & search_condition
        elif search:
            condition = self._set_search_()

        if condition:
            querys = querys.where(condition)
        
        # 如果有的表数据量比较大,查询起来特别慢,就可以手动指定一个值,加快速度
        if kwargs.get("total") is not None:
            total = kwargs["total"]
        else:
            total = querys.count()
        querys = querys.paginate(page, per_page)
        
        # 构建最终的json文件
        data = [x for x in querys.dicts()]

        return {
            "data": data,
            "draw": draw,
            "start": page,
            "length": per_page,
            "recordsTotal": total,
            "recordsFiltered": total
}

你可能感兴趣的:(datatables折腾日志)