2014年5月14日水曜日

django (python)とJavaScriptのミリ秒単位の時間の受け渡し

はじめに

PythonのWebアプリケーションdjangoで管理している時間・時刻をJSON形式でJavaScriptに渡したり,あるいはJavaScriptからAjaxを使ってJSON形式を介してサーバ側のdjangoに渡したりします.
ここでは,年月日及び時分秒に加えミリ秒でのやりとりも行います.

サーバ側:データベースモデルの確認と準備

まず,バックエンドのデータベースシステムのDateTime型など時間型のフォーマットがミリ秒やマイクロ秒までサポートしているかどうか確認してください.MySQLでは5.6.4からマイクロ秒までをサポートしているようです.

MySQL 5.6.4 and up expands fractional seconds support for TIMEDATETIME, and TIMESTAMP values, with up to microseconds (6 digits) precision(引用 2014年5月14日)

ただ,ここではバックエンドのデータベースがミリ秒やマイクロ秒までをサポートしていない物として進めます.
そこで,DateTimeフィールドの他に,マイクロ秒を記録するフィールドを作成します.

# models.py
from django.db import models

class TimeSample(models.Model):
''' マイクロ秒を記録するために整数型フィールドのdatetime_microを設けます '''
    datetime = models.DateTimeField()
    datetime_micro = models.IntegerField(default=0)

クライアント側: dateformat.jsの準備

JavaScriptで,サーバからString型の日時を受け取りDate型に変換する必要があります.変換にはdateformat.jsが便利です.dateformat.jsはMITライセンスで配布されていますので,以下からダウンロードしましょう.

http://www.enjoyxstudy.com/javascript/dateformat/


いつもの通り,HTMLから読み込みます.
<script type="text/javascript" src="{{ static_url }}scripts/dateformat.js"></script>

クライアント側:jQueryとjQueryのJSONプラグインの準備

JavaScriptでサーバと通信したり,JSON形式を送信用に作成したり,受信時に解析したりするためにjQueryとそのプラグインを使います.以下からそれぞれダウンロードしましょう.

jQuery本体 http://jquery.com/
jQueryのJSONプラグイン https://code.google.com/p/jquery-json/downloads/list

で,HTMLに追加;;
<script type="text/javascript" src="{{ static_url }}scripts/json-x.xx.x.js"></script>
<script type="text/javascript" src="{{ static_url }}scripts/json.json-x.x.js"></script>

JavaScriptからPythonに時間を渡す(Ajax)

クライアント

クライアントからJavaScriptでサーバ側のdjango(python)に日時を渡します.jQueryの$.ajaxから非同期バックグラウンドで送信しましょう.(POSTで送る場合,CSRF関連があるかもしれませんが,個々では割愛します.)

ここでは,送信時のDateフォーマットを""yyyy-MM-dd HH:mm:ss:SSS"とします.SSSはミリ秒です.
// JavaScript
// 送信用フォーマットの日付を作成

var date = new Date(); var dateFormat = DateFormat("yyyy-MM-dd HH:mm:ss:SSS"); var dateStr = dateFormat.format(date); var sendData = {'datetime': dateStr}; // 送信 $.ajax({ url: "http://www.xxxx.com/", type: "POST", contentType: "application/json; charset=utf-8", datatype: "json", data: $.toJSON(sendData), success: function(data) { alert("送信成功"); }, error: function() { alert("失敗"); } });

サーバ側

クライアントからJSON形式で送られてきた日時の情報を取得し,データベースに記録します.

# views.py
from models import *
from datetime import datetime
from django.http import *
import json as simplejson

def requested_json_datetime(request):
    ''' リクエストされた日時をデータベースに記録します '''

    data = simplejson.loads(request.body)
    time = datetime.strptime(data[u'datetime'], '%Y-%m-%d %H:%M:%S:%f')
    
    model = TimeSample(datetime=time, datetime_micro=time.microsecond)
    model.save()

    return HttpResponse("success")
実際にはミリ秒ですがサーバ側ではマイクロ秒のオーダーで保存します.

PythonからJavaScriptに時間を渡す

今度はサーバ側からJavaScriptにJSONで時間を渡してみましょう.

クライアント側

サーバに「時間くださいよ~!」ってリクエストしましょう.取得したJSONからDate型を作成します.
var dateFormat = new DateFormat("yyyy-MM-dd HH:mm:ss:SSS");
var date = dateFormat.parse(responsedData.datetime);

サーバ側

データベースを参照してDateTime型の値を取得します.strftime()関数でDateTimeを文字列型に変換できます.変換するときのフォーマットの指定方法はhttps://docs.python.org/2/library/datetime.html#strftime-strptime-behavior で紹介されています.
例えば次のようにすると,送信時とほぼ同じフォーマットの文字列を作成できますが,%fはミリ秒ではなくマイクロ秒であるという違いがあります.

sample = TimeSample.objects.get(id=1)
dt = sample.datetime
dt = dt.replace(microsecond=sample.datetime_micro)
str = dt.strftime("%Y-%m-%d %H:%M:%S:%f")


これではマイクロ秒でレスポンスしてしまいますので,ここでは手作業で変換しました.単純に1000で除算してマイクロ秒からミリ秒に変換しています.
sample = TimeSample.objects.get(id=1)
dt = sample.datetime
dt = dt.replace(microsecond=sample.datetime_micro)
str = "%04d-%02d-%02d %02d:%02d:%02d:%03d" % (
                                    dt.year,
                                    dt.month,
                                    dt.day,
                                    dt.hour,
                                    dt.minute,
                                    dt.second,
                                    dt.microsecond / 1000,
                                    )

ではstrをJSONにしてレスポンスしましょう.


data = {'datetime': str}
json = simplejson.dumps(data)
return HttpResponse(json)

総括

JavaScriptとPython間で日時のやりとりを行いました.年月日,時分秒までならかんたんなのですが,ミリ秒までのやりとりだと一ひねりいりますね.