Django + datatables & 100万件表示

## Django + datatables & 100万件表示
Djangoのセットアップ、プロジェクト作成、アプリ作成は他サイトを参照ください

django-datatables-viewなる便利なライブラリが存在するので、ありがたく使わせていただきます
```
pip install django-datatables-view
```

今回はajaxのデバッグに便利な、runserverではなく、runserver_plus（django-extensiokn）オプションで開発サーバーを起動
```
python manage.py runserver_plus 192.168.11.7:8089
```

urlの設定
```dashboard/urls.py

path('list/', views.FileDataTablesView.as_view(), name='ajax_datatable'),

```


```template/common/base.html
<html>
<head>
.........
<link rel="stylesheet" href="https://cdn.datatables.net/2.0.8/css/dataTables.dataTables.css" />
<link rel="stylesheet" href="https://cdn.datatables.net/2.0.8/css/dataTables.bootstrap5.css" />
<script src="//code.jquery.com/jquery-3.5.1.js"></script>
.........
</head>
<body>

{% block content %}{% endblock content %}

{% block javascripts %}{% endblock javascripts %}
<script src="https://cdn.datatables.net/2.0.8/js/dataTables.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.2/moment.min.js"></script>
</body>

</html>
```
最新バージョンは
https://cdn.datatables.net/releases.html
で確認ください

``` template/dashboard/index.html
{% extends "common/base.html" %}
・・・・・・・・・・
{% block content %}
<table id="datatable" class="table table-striped fs-10 mb-0">
<thead>
<tr class="bg-white item_table">
  <th style="width: 10%;">種類</th>
  <th style="width: 20%;">ファイル名</th>
  <th style="width: 20%;" class="text-center">アップ日</th>
  <th style="width: 10%;">容量</th>
  <th style="width: 40%;">メモ</th>
</tr>
</thead>
<tbody class="item_table"></tbody>
</table>
{% endblock content %}
{% block javascripts %}
<script>
  var table_option = {
    order: [[5, 'desc']],  //targets：Nのカラムで並べる（ここではid）
    searching: false,      //標準の検索機能を無効（検索機能を設定するのでこっちは無効にする）
    deferRender: true,     //大量データの場合、性能が向上（みたい）
    serverSide: true,      //サーバーサイド連携をおこなう設定
    processing: true,      //tableのレンダリング時に「処理中」を表示（falseでもOK）
    lengthChange: false,   //表示件数選択フォームの無効化（割とウザい）
    pageLength: 100,       //表示件数固定(お好きな数値で）
    pagingType: "numbers", //ページングの表示形態
    ajax: {
      "url": "{% url 'dashboard:ajax_datatable' %}",  //urls.pyで指定したurl設定
      "type": "GET",
      "data": function ( d ) {
         return $.extend( {}, d, {
           "KeywordInput": $("#KeywordInput").val().toLowerCase(),  //検索語を小文字に変換
         } );
       }
    },
    columnDefs: [
      {targets: 0,  data: 'type'},
      {targets: 1,  data: 'file_name'},
      {targets: 2,  data: 'created',
        render: function ( data ) { //データの変換、修飾は関数で処理
            return moment(data).format("YYYY-MM-DD H:mm:ss"); //createdはマイクロ秒、秒単位に変換
        }
      },
      {targets: 3,  data: 'file_size', className: 'text-end pe-5', //classnameでclass指定可能
        render: function ( data ) {
            return Number(Math.floor((data/1024)*10)/10).toLocaleString() + 'Mb';
        }
      },
      {targets: 4,  data: 'memo',
        render: function ( data ) {
            return '' +
                '<div class="row">' +
                '<div class="col-6 text-start"> ' +
                data +
                '</div>'+
                '<div class="col-6 text-end">' +
                '<i class="fas fa-edit"></i>' +
                '</div></div>';
        }
      },
      {targets: 5,  data: 'id', visible :false }, //並べ替えと次画面遷移のGET変数用、visible falseで非表示
    ]
  }

  $(function(){
    $.extend( $.fn.dataTable.defaults, {
		language: {
			url: "https://cdn.datatables.net/plug-ins/2.0.8/i18n/ja.json", //日本語化
		}
	});
    var table=$("#datatable").DataTable(table_option);

    // 再描画
    $('#KeywordInput').bind("keyup change", function(){
      table.draw();
    });

  });
</script>
{% endblock javascripts %}
```

```views.py
from django_datatables_view.base_datatable_view import BaseDatatableView
from django.db.models import Q //Qオブジェクトやっぱ便利


class FileDataTablesView(LoginRequiredMixin, BaseDatatableView):
  model = File
  # 初期表示
  def get_initial_queryset(self):
    return (
      super().get_initial_queryset().
      select_related('custom_user').
      filter(
        deleted__isnull=True,
        custom_user=CustomUser.objects.get(pk=self.request.user.pk),
      )
    )

  # 検索結果
  def filter_queryset(self, qs):
    keyword = self.request.GET.get(u'KeywordInput', None)
    if keyword:
      keywords = keyword.split()
      for search in keywords:
        qs = qs.filter(
          Q(memo__icontains=search) |
          Q(type__icontains=search) |
          Q(created__icontains=search) |
          Q(file_size__icontains=search) |
          Q(file_name__icontains=search)
        )
    return qs

```

### 大量データのパフォーマンス
環境
Django 4.2.14
MariaDB 10.5.22
Rocky Linux release 9.3 (Blue Onyx)

ハード
Intel(R) N100 3.4GHz 8GB/256GB


+ 1万件超の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/4022a288-46fd-449b-7e53-d14adaf9bd50.png)

![2024-08-31 16.37.35.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/0f6cfda9-5a65-4fed-e93c-a5b12c877763.gif)

+ 10万件超の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/e6a0c177-93ad-c237-76e5-3775e5ac1e49.png)
![2024-08-31 16.43.17.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/5d36f6c4-ef06-1bfd-10eb-6851017ef2c2.gif)


+ 100万件超の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/c07999cc-6968-f2e1-459d-2ad126ee9331.png)
 
![2024-08-31 16.49.03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/9a6fb35b-123d-569f-aa5d-0cd50f32af9c.gif)

+ 1,775,038件で検索
![2024-08-31 17.20.27.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/4d13fae4-fd72-d2d9-9e37-e05240a3aa07.gif)

※ datatablesの最大行数は16,777,216 行(2の24乗）
※ datatablesの最新バージョンは2.1.5（2024-08-31現在）