Bootstrap:daterangepickerで選択しPOSTしてMYSQLの値を抽出する方法

Bootstrap:daterangepickerから選択した値をPOSTしてMYSQLの値を抽出する方法

前回記事はこちら。

今回、DateRangePickerでの日付指定からデータべ―ス抽出まで悩んだのでそこら辺をまとめて置こうと思います。

DaterRangePickerの想定している基本的な実装

全てをJavaScriptの中で処理すべく構成していると思われます。

いわゆる Ajax = 画面遷移を伴わないデータの抽出/変更 です。

その為、Views.py にPOSTするという考えを持たなくてもいい構成(JavaScript内で抽出してしまう構成)がDaterRangePickerを最も生かした良い実装なのだと思います。

今回行った実装について

今回実装していく中でJavaScriptで期間抽出まで実装するには大きな問題があると考え構成を変えました。
具体的には以下の通りです。

  1. DaterRangePickerで設定した期間をViews.py にPOSTする
  2. POSTされた値を使い、MySQLの抽出を行う
  3. 抽出された値をグラフ用の形に成型
  4. テンプレートに値を送りグラフ描写

JavaScript内での抽出を取りやめた理由

単に抽出したいDBのサイズが大きくなる可能性があるからです。

例えば毎日、端末ごとの売上データを蓄積したとして1年間で1095件(365×3)。
出店しているモールが3つあったとして3285件(1095×3)。
更に2年間推移を出そうとした場合6570件(3285×2)。

これをJavaScriptで期間抽出するよりはSQLで行ってしまった方がいいだろうと考えました。

Veiws.pyにPOSTして抽出を変える事で発生した問題点

指定した期間を表示してくれていた箇所に、毎回初期値が入るようになりました。

$('#reportrange span').html(moment().subtract(29, 'days').format('MMMM D, YYYY') + ' - ' + moment().format('MMMM D, YYYY'));

これは抽出後に画面遷移が入るため、Ajax側で取込んだレンジがクリアされる事が原因です。
その為、誤った表示がされるよりはと下の様に変更しました。

 $('#reportrange span').html('期間を設定してください');

期間抽出しても毎回カレンダーは初期化されてしまうのでイマイチしっくりこないのですが...。
なんかいいやり方あると思うんですけど、動くところまでまず作り込みたいので一旦ここで線引きしました。

確定ボタンクリックでJavaScriptからVeiws.pyにPOSTする方法

POST送信の手段は2つあります。

  1. AjaxでPOSTする
  2. FormでPOSTする

詳しくは後程記載します。
まずは【daterangepicker】での悩みどころについて記載します。

vender/bootstrap-daterangepicker/daterangepicker.jsに Formタグが存在しない

Djangoのテンプレートでは id="daterrangepicker"で呼び出されていました。

このidでbuild/js/custom.js の該当箇所が呼び出され、vender/bootstrap-daterangepicker/daterangepicker.js に記載されているHTMLを吐き出すと言う構造です。
そして、問題はこのdaterangepicker.js内のHTMLには馴染みの深いFormタグが無い点です。
となれば、Formタグを使わない形でPOSTするか、JavaScript内でForm+inputを作って送るかの2択。
それが上記の【Ajaxで送信する】【Formで送信する】の2つです。

AjaxでPOSTする

Ajaxの場合、画面遷移を伴わずに画面上の値を可変させます。
つまりは、グラフの姿を変えるにはJavaScript上で抽出条件を変えてあげないといけません。
この事実に気付かずAjaxでの実装を行ってしまい「何故だ...」となったのが今回の顛末でした。

JavaScriptからのPOST送信 ※Veiws.pyで受け取る

    $('#reportrange').on('apply.daterangepicker', function(ev, picker) {
        # 送信するデータを作る
        let data = [
                   "from": picker.startDate.format('YYYY/MM/DD'),
                   "to": picker.endDate.format('YYYY/MM/DD')
                   ]
        # jsonでエンコードする
     data = JSON.stringify(data);

        //非同期通信で情報送信
        $.ajax({
            type: "POST",
            //今いるURLに対して送信指示
            url: location.pathname,
            data: data,
            }).done(function (results) {
                // 通信成功時の処理
                $('#text').html(results);
                console.log("URL : " + url);
                console.log("results : " + results);
            }).fail(function (jqXHR, textStatus, errorThrown) {
                // 通信失敗時の処理
                alert('ファイルの取得に失敗しました。');
                console.log("ajax通信に失敗しました");
                console.log("jqXHR          : " + jqXHR.status); // HTTPステータスが取得
                console.log("textStatus     : " + textStatus);    // タイムアウト、パースエラー
                console.log("errorThrown    : " + errorThrown.message); // 例外情報
                console.log("URL            : " + url);
            });
        return false;
        //コンソールにログを出す ※初期値で入っていた処理
        console.log("apply event fired, start/end dates are " + picker.startDate.format('YYYY/MM/DD') + " to " + picker.endDate.format('YYYY/MM/DD'));
    });

AjaxでのPOST送信を受け取る ※Veiws.py

import json

# csrfでの接続エラーを防ぐ
@csrf_exempt

def DetailView(request):
  (中略)

  try:
    json_object = json.loads(request.body)
  except ValueError as e:
    params = {
      'start': datetime.datetime.today(),
      'end': datetime.datetime.today()
    }
    return render(request, 'surprise.html', params)
  else:
    # JSON文字列
    datas = json.loads(request.body)
    #datas = request.body
    params = {
      'start': datas["from"],
      'end': datas["to"]
    }
    return render(request, 'test.html', params)

Ajaxで送信した場合、値は【request.POST】ではなく【request.body】に届きます。
暫くこれに気付かず「何故だ、なぜできない!」ともがいてました。

9行目『Try ~ except ValueError ~ else 』はAjaxで値が送られてきていない時の回避措置です。
このTryからの文が無い場合、画面を読み込んだ段階で「Ajaxから値が届いてないぞ」とエラーが発生します。

Expecting value: line 1 column 1 (char 0)

こんなエラーを出さない為の処理が下の処理です。

  • except ValueError:Ajaxからの送信がない時の処理
  • else以降:Ajaxからの送信を受けた時の処理

基本的にはこの通りなのですが、Ajaxの基本を思い出しましょう。

Ajax=画面遷移を伴わずに画面上の値を可変させる

views.py が呼び出されるのはurls.py からの操作なので、URLの呼出し時に読み込まれています。
なので、このように書いても『今カレンダー選択したから Ajax送信してelse以降が呼び出される』と言う事は無いんです。つまりは、延々とグラフの変化は起きないと...。

言われてみればその通りなんですけど、今回の実装では全くの盲点でした。

Ajaxで送った値でグラフを可変させたいのであれば、必要となる要素を全てJavaScriptに送りJS上で抽出となります。この場合「そもそもVeiwsに送る意味ある?」と思ってしまいました。

FormでPOSTする

この場合はカレンダーで抽出した後、リロードするのでVeiws.py に値は送られグラフの変化も発生しますがカレンダーが元に戻ります。
つまりは、カレンダーとグラフの範囲に互換性が無くなります。
※カレンダーは抽出の道具としての用途だけになる。

JavaScriptでFormタグを作りPOSTする

    $('#reportrange').on('apply.daterangepicker', function(ev, picker) {
        //formタグを作成する
        let f = document.createElement('form');
            f.method = 'post';
            f.action = location.pathname;
      //formタグ内にHTML(inputタグ)を配置する
            f.innerHTML = '<input type="text" name="from" value='+  picker.startDate.format('YYYY/MM/DD') + '>' +
               '<input type="text" name="to" value=' + picker.endDate.format('YYYY/MM/DD') + '>' +
               '<input type="submit" value="submit">';

            //テンプレート内の body に作成したFormタグ『f』を配置する
            document.body.append(f);
        //『f』を form送信する
        f.submit();

        //コンソールにログを出す ※初期値で入っていた処理
        console.log("apply event fired, start/end dates are " + picker.startDate.format('YYYY/MM/DD') + " to " + picker.endDate.format('YYYY/MM/DD'));
    });

FormでのPOST送信を受け取る ※Veiws.py

from django.views.decorators.csrf import csrf_exempt

# csrfでの接続エラーを防ぐ
@csrf_exempt

def DetailView(request):
  (中略)
 """Form Post受取り"""
  std = request.POST.get('from')
    edd = request.POST.get('to')
    startdate = datetime.datetime.strptime(std +" 00:00:00", '%Y/%m/%d %H:%M:%S')
    startdate = datetime.date(startdate .year, startdate .month, startdate .day)
    enddate = datetime.datetime.strptime(edd +" 00:00:00", '%Y/%m/%d %H:%M:%S')
    enddate = datetime.date(enddate .year, enddate .month, enddate .day)
   
    params = {
      'start': startdate,
      'end': enddate
    }
    return render(request, 'test.html', params)

日付返還まで書き残しておきました。
FormでのPOSTは【 request.POST.get('送信時のname') 】で受け取る事が出来ます。
でも、こいつはテキストとして受渡されるので日付計算やdate抽出などを行う場合はdatetime型に変換してあげる必要があります。

datetime.datetime.strptime()でまず変換するのですが、この時に時間データも必要になる様です。
最初、時間なしで記載したのですがエラーになりました。
で、面倒なのがもう一個、日付の指定文字がJavaScriptと違う事。ちゃんと言語に合わせましょう。

まとめ

速足のメモになりましたが、こうやってまとめて置けば次は実装前に諸々気付くだろうと。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。