[Django] 常時Ajaxで動く検索画面を１時間で作ろう！ jQuery Datatables サーバ連携機能の紹介 【サンプルアプリ有り】

## １．この記事について

この記事は [Django Advent Calendar 2018](https://qiita.com/advent-calendar/2018/django) 12月19日の記事です。

たいていのアプリには検索一覧画面が必要ですが、使いやすさのために並べ替えやページ分割機能などを追加していけば当然開発工数がふくらみます。

そこで、この記事ではjQueryプラグイン「[DataTables](https://datatables.net/)」の[サーバ連携機能(Server-Side Processing)](https://datatables.net/examples/server_side/simple.html)を紹介します。検索画面のUIを簡単に実装するDataTablesと、各フレームワーク向けに提供されているサーバ連携機能用ライブラリを組み合わせることで、簡潔なコードで高機能な検索一覧画面を作ることができます。

この記事ではWebアプリケーションフレームワークとしてPythonのDjangoを使った例を紹介しますが、**サーバ連携機能用ライブラリはRails、Larabelでも公開されています**。おそらく同じ要領で実装可能ですので、是非試してみてください。

- **DataTables サーバ連携機能用ライブラリの一覧**

| フレームワーク | ライブラリ |
|:--|:--|
|Django|[django-datatables-view](https://pypi.org/project/django-datatables-view/)|
|Ruby on Rails|[ajax-datatables-rails](https://github.com/jbox-web/ajax-datatables-rails)|
|Larabel|[Laratables](https://laravel-news.com/laratables)|

### サンプルページ

実際の動作を確認できるサンプルページを用意しました。

- [サンプル１：ＰＣ用](https://django-datatables-view-sample.herokuapp.com/sample01)
 - 通常の検索に加えExcel・CSVダウンロード、印刷機能が確認できます。
- [サンプル２：モバイル用](https://django-datatables-view-sample.herokuapp.com/sample02)
 - レスポンシブ対応機能のデモ。スマホで見るか画面幅を狭めて確認してください。

### バージョン

- Python 3.7.0
- Django 2.1.3
- django-datatables-view 1.17.0
- django-excel-response 2.0.3
- jquery.dataTables 1.10.19
- twitter-bootstrap 4.1.1

## ２．画面イメージ

実際の動作はサンプルページかサンプルアプリで確認できます。

サンプル１：ＰＣ用
![localhost_8000_sample01(iPad) (2).png](https://qiita-image-store.s3.amazonaws.com/0/217094/a85b13e5-909d-6c24-4b7d-ba3217112a86.png)

サンプル２：モバイル用

DataTablesはレスポンシブデザインに対応しています。
幅の制限で表示しきれなくなった情報は詳細行に移動します。「丸に＋」のマークをタップすると折りたたまれた詳細行が開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/69575d16-da06-b12a-3a9c-bd0fb40de40a.png)

サンプル３：タスク管理ツール

DataTablesにデータの追加・更新機能を付けました。
入力フォームにはBootstrapのモーダルフォーム、バックエンドにはDjango RestAPI Frameworkを利用しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/5727e828-4bf6-2057-e04f-61790cfd506d.png)



## ３．サンプルアプリの使い方

実際に動かして試すことのできるサンプルアプリ（Djangoのプロジェクト）を用意しました（要：Python 3.5以上）。

https://github.com/okoppe8/django-datatables-view-sample

ReadMeの内容に従ってセットアップしてください。**venv で仮想環境を作るので本体の環境に影響はありません。**


データはこちらのページのものを使わせていただきました。

- [市区町村役場住所一覧](http://www.geocities.jp/takevv/adrs.html)

なおDjangoについての基本的な情報はこちらにまとめたので参考にしてください。

- [Django 参考資料リンク](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14#%E5%89%8D%E6%8F%90%E7%9F%A5%E8%AD%98%E5%8F%82%E8%80%83%E8%B3%87%E6%96%99)

## ４．DataTablesとサーバ連携機能について

- [DataTables 公式](https://datatables.net/)

DataTablesはデータグリッドを表示するためのjQueryプラグインです。データソースとして既存のtable、JSONファイル、JSONを提供するWebAPIが使えます。機能や表示オプションが充実している事に加え、jQueryなので容易に扱うことができます。

この手のプラグインには共通の欠点があり、それは**データを一度に読み込む必要があり、扱うデータが千件ちかくなると処理が重くて使い物にならなくなる**という点なのですが、DataTablesはその問題についてサーバ連携機能(Server-Side Processing)という解決策を用意しています。

- [DataTables公式 Server-side processingのサンプル](https://datatables.net/examples/server_side/simple.html)

これは**DataTablesの仕様に沿ったバックエンド（WebAPI）を用意すると、検索・ソート・ページ分割処理をサーバ側で行われるので、最小限の処理コストで表示が更新されるようになる**というものです。これによりデータ件数が多くても気兼ねなくDataTablesが利用できます。

このWebAPIを自前で実装するのは面倒なので、[設定より規約（convention over configuration）方式](https://ja.wikipedia.org/wiki/%E8%A8%AD%E5%AE%9A%E3%82%88%E3%82%8A%E8%A6%8F%E7%B4%84)によりORMのモデルからWebAPIを実装するライブラリが有志によって公開されています。このライブラリを利用して短いコードで高機能な検索画面が作成できるというわけです。

## ５．検索画面の作り方

Djangoでのサーバ連携機能の実装方法を説明します。前提としてモデル・データベース・マスタデータは既に作成済みとします。

- Djangoのプロジェクトの作り方とモデルの定義方法はこちらを見てください。
 - [[Python] Djangoチュートリアル - 汎用業務Webアプリを最速で作る](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14)
- マスタデータの簡単な作成方法については以下の記事を参照
 - [[Django] マスタデータの作り方](https://qiita.com/okoppe8/items/fb81b96c88ae1c7623e1)

### バックエンドを作る

まずバックエンドとなるのWebAPIを作ります。Djangoではライブラリ「django-datatables-view」を使って実装します。

- **[pypi: django-datatables-view](https://pypi.org/project/django-datatables-view/)**

django-datatables-view の利用方法は簡単です。まずパッケージをインストールします。

```
pip install django-datatables-view
```

次にDjangoのView（一般的なMVCでのControllerに相当）を実装します。

```python:views.py

from django.views import generic
from django_datatables_view.base_datatable_view import BaseDatatableView

from .models import Item


class ItemsJsonView(BaseDatatableView):
    # モデルの指定
    model = Item
    # 表示するフィールドの指定
    columns = ['id', 'pref_name', 'name', 'furigana', 'zipcode', 'address', 'tel', 'code']

    # 検索方法の指定：部分一致
    def get_filter_method(self):
        return super().FILTER_ICONTAINS

```

Viewはdjango_datatables_viewが提供するClass-Based-Viewの「BaseDatatableView」を継承して作ります。主な設定項目は以下の通りです。

- **model**
 - 検索対象のモデルを指定
- **columns**
 - 表示させるフィールドを選択
- **def get_filter_method**
 - 検索方法。デフォルトは「FILTER_ISTARTSWITH（前方一致）」。「FILTER_ICONTAINS（部分一致）」に変更可。

※ ここからサンプルコード外

- **def get_initial_queryset**
 - 初期クエリセットを指定する。たとえば**論理削除データは表示しない、管理者以外は自分のデータのみ表示**という要件はここで実現可能。
 
- **def filter_queryset**
 - 検索処理をカスタマイズできる。自前で検索フォームを用意して複雑な検索を行うという用途に使う。このメソッドの使い方についてはいつか別の記事を書きたいと思う。

- **def prepare_results**
 - 出力する前にデータを加工する。なおフロントエンド部でもデータの加工は可能(renderプロパティ)。

他にもいくつか項目があるので公式サイトで確認してください。
定義ができたらurls.py(ルーティングテーブル)にエンドポイントを登録します。

```python:urls.py
urlpatterns = [
    path('items', views.ItemsJsonView.as_view(), name='ItemsJson'),
]
```

### フロントエンドを作る

バックエンドが完成したらテンプレート（一般的なMVCにおけるViewに相当）を作成します。
HTML上にtableタグを配置し、必要なライブラリをlink・scriptで定義し、JavaScriptで以下のコードを書きます。問題がなければDataTablesにデータが表示されるはずです。

参考：https://datatables.net/examples/server_side/simple.html

```JavaScript

$(document).ready(function() {
    var oTable = $('.datatable').dataTable({
        // ...
        "processing": true,
        "serverSide": true,
        "ajax": "items" // 上で作成したエンドポイント
    });
    // ...
});
```

### DataTables 参考リンク

DataTablesには非常に多くの機能があります。基本的な設定方法や応用的な使い方については公式サイトやサンプルコード、以下のリンクで調べていただきたいと思います。

- やるぞう
 - [DataTables 超入門]()
 - [DataTables の使い方 1 スクリプトの設置と基本の使い方](https://yaruzou.net/js-datatables-b1)
 - [DataTables の使い方２『ページ送りとスクロール』](https://yaruzou.net/js-datatables-b2)
 - [DataTables の使い方3『ソートとサーチオプション』](https://yaruzou.net/js-datatables-b3)
 - [DataTables の使い方４『テーブルデザイン(スタイリング)』](https://yaruzou.net/js-datatables-b4)
 - [DataTables が動かない時のチェック項目](https://yaruzou.net/js-datatables-trouble)
 - [Datatables で通貨文字やカンマを扱う](https://yaruzou.net/datatables-a1)

- Qiita
 - [逆引きDataTables](https://qiita.com/tq_jappy/items/a256b74db3ff2f9071f2)
 - [DataTablesの使い方](https://qiita.com/nissuk/items/7ac59af5de427c0585c5)


## ６．レポート機能を自作する

DataTablesには「**Buttons**」という拡張機能があります。

参考：[公式サイトのボタン機能のサンプル](https://datatables.net/extensions/buttons/examples/initialisation/simple.html)

これは選択中のデータを**Excel・CSV・PDF・クリップボードにコピー・印刷するためのボタンを追加する**という便利な機能です。しかし残念ながらこのサーバ連携機能での動作時には使えません。ページをまたいで選択状態を保存するという機能がないのです。

ですがもともとこの機能は表現面で制約が多く、あらためて考えるとそれほど実用的な機能ではありません。そこで、より自由な表現ができるようにサーバサイド側でレポート機能を実装してみました。

### Excel・CSV出力

DjangoでExcelファイルのダウンロードを簡単に実装するなら「django-excel-response」が便利です。

- **[django-excel-response](https://pypi.org/project/django-excel-response/)**

例えばデータを表示するMyListViewがすでにあったとして、同じデータをダウンロードさせるViewのコードは以下の通りです。

```python:views.py
from excel_response import ExcelMixin
class ExcelView(ExcelMixin, MyListView):
   pass
```

**これだけ。**非常にシンプルです。

ただ、このままだとExcelの見出し行はフィールド名がそのまま表示されてしまうので、日本語の見出しをつけるには以下のようにコードを追加してください。ファイル名の変更等もできます。設定方法は公式サイトで確認してください。
 
```python:views.py
class ExcelView(ExcelMixin, MyListView):
    # 見出し行を日本語にする
    def get_queryset(self):
        header = [['id', '県名', '市町村名', 'ふりがな', '郵便番号', '住所', '電話番号', '自治体コード']]
        body = [list(entry.values()) for entry in super().get_queryset().values()]
        return header + body

```

`force_csv = True`という設定でcsvでの出力にも対応します。このcsvの文字コードはBOM無しutf-8です。直接 Excelで開くと文字化けするので注意してください。

### 印刷


サンプルアプリの印刷機能はラベルシールのシートへの印刷用です。[こういうシート](http://www.a-one.co.jp/product/search/detail.php?id=28382)に郵便物に貼るための宛名を印刷するイメージですね。

印刷画面はこちらの記事を参考に作りました。

- [Qiita:そろそろ真面目に、HTMLで帳票を描く話をしようか](https://qiita.com/cognitom/items/d39d5f19054c8c8fd592)
- [Qiita:紙の帳票からHTML帳票を作る方法](https://qiita.com/okoppe8/items/45e7b37774899e763d31)

HTMLで印刷機能を作る場合、Webの技術がそのまま使えます。たとえば[バーコード](https://github.com/jbdemonte/barcode)や[QRコード](https://github.com/jeromeetienne/jquery-qrcode)が簡単に使えるというメリットがあり非常に便利です。

### PDF出力(断念)

~~こちらの記事を参考にPDFファイルの出力を実装しようと思ったのですが、残念ながら私の技術ではどうにも日本語の文字化けが解決できず断念しました。うまく出力できた人がいたらやり方を教えてください m(_ _ )m。~~

xhtml2pdfを使ったPDFの印刷は可能でした！
結論から言うと、以下のエントリの方法でＯＫです。

- [DjangoのDetailViewをPDFで出力する(日本語対応)](https://qiita.com/t-hiroyoshi/items/2f6cb40130e1729f806a)

注意してほしいのは、フォントファイルを配置する場所はアプリ内のstaticディレクトリではダメで、ちゃんと`settings.STATIC_ROOT`を設定し、対応するディレクトリを作成してそこに配置する必要があります。

ただしxhtml2pdfをある程度試してみましたが、スタイルシートがまともに機能しないためデザイン性のある帳票の作成は断念せざるを得ませんでした。多少の変更でHTMLをPDFに変換できるというイメージは甘かったようです。

もしPDF出力が必要なら[WeasyPrint](https://qiita.com/search?q=WeasyPrint)を使う方法もあるようです。こちらはpip外のモジュールを別途OSにインストールする必要がありますが、xhtml2pdfよりもデザイン性の高いものに対応できるようです。


##７．データの更新方法

当然、登録画面や更新画面を作ればデータの更新はできますが、Djangoには`管理アプリ`という裏技があります。

これは簡易CMSとして使えるようにDjangoのプロジェクトにはじめから付属しているアプリで、定義したモデルの登録・更新・削除が優れたＵＩで利用できます。

この機能は以下の手順で有効になりますので、Djangoを使ったことが無い方は是非試してみてください。

１. ターミナルより以下のコマンドを実行し管理者のIDとPassを入力する。

- windows

```
env\Scripts\activate
manage.py createsuperuser
```

- MacOS

```
source env/bin/activate
python manage.py createsuperuser
```

E-mailの設定は未入力でよい。

２. ブラウザで「localhost:8000/admin」にアクセスし作ったID/Passを使う。

##８．DjangoでWebアプリをカジュアルに使おう

サンプルアプリのように、Djangoのプロジェクトはこのような形で簡単に他人に実行してもらうことができます。これはPythonの標準モジュールの中にhttpサービスがもともと組み込まれていることと、Pythonは実行環境を容易に分離できるということのおかげです。

この特長を生かしてWebアプリを一般ユーザーにもっとカジュアルに使ってもらうことはできないかということを考えています。

サンプルアプリでセットアップのコマンドをいくつか実行しましたが、これはまとめてスクリプト化できます。さらにhttpの起動とブラウザの立ち上げもスクリプト化し、適当なショートカットを作ってアイコンを変えてしまえば、デスクトップアプリと同じように使ってもらえるのでは？と思うのです。

個人的に興味があるのは`Microsoft Access`や`FileMaker`が対象とする業務をこの方法でカバーできないだろうか、ということです。

今回紹介した方法のようにJavascriptのプラグインやフレームワークを活用すれば開発効率はAccessやFileMakerに劣りませんし、はるかに自由なコーディングが可能です。Gitで管理できるというメリットも生まれますし、当然**ライセンス料が不要・ＯＳに関係なく動く**といったメリットは一般層にも強くアピールできると思います。

予算不足でライセンスがまとも買えないという現場が多い中、なんとか業務改善を進める方法として提案させていただきます。ぜひ皆さんの意見を聞かせてください。