django - javascript 間を JSON でやりとりする

ちょっと必要にかられたのでやってみたら、思ったより手間取ったのでメモしておく。

手間取った理由その1

0.97pre-SVN-6883 だと、django.utils.simplejson でモデルオブジェクトを JSON にできなくなってる。0.96のころはできたように思うんだけど。unicodeブランチのマージの影響か?仕方ないので django.core.serializers でシリアライズしてみたら、JSONとしては使いずらい結果になる。まあシリアライズだから当然か。子要素が展開されずにIDだけというのも困りもの。DTO的に使うのは不向きっぽい。

悩んでてても解決しそうになかったので、HTMLと同じようにJSONもテンプレートでレンダリングすることにした。同じようなことを考える人はいるもので、Django snippets にいいのがあった。

あとはこんなテンプレートを用意してやればいけた。ただし、IE関係は注意が必要。

  • キャッシュが効いてるみたいなので、パラメータクエリに時刻とかのランダムな値を追加しないとだめっぽい。
  • 閉じ括弧直前の「,」を除いておかないとエラーになる。
  • 文字列はとりあえずダブルクォーテーションで囲む。
  • boolean は lower フィルタを通しておく。
{
  "entities" : [
    {% for entry in entities %}
    {
      "id" : "{{ entity.id }}",
      "name" : "{{ entity.name }}"
    }{% if not forloop.last %},{% endif %}
    {% endfor %}
  ]
}

手間取った理由その2

pythonjavascriptで、datetimeのミリ秒以下の扱いが違って困った。あと、ミリ秒まで含む文字列からpythonのdatetimeに変換する方法がわからず、かなり困った。

文字列からpythonのdatetimeへの変換は、文字列から日付型変換と演算 - mitszoの日記を参考にして解決した。ミリ秒まで欲しかったので、ちょっと力技。

from datetime import datetime, timedelta
import time

def str2datetime(datestr):
   ''' ミリ秒まで含む日時形式の文字列をdatetimeに変換します。
       @param datestr yyyymmddHHMMSSsss形式の文字列
   '''
   base = datetime(*time.strptime(datestr[:-3], '%Y%m%d%H%M%S')[:6])
   ms = timedelta(microseconds=int(datestr[-3:]) * 1000)
   return base + ms

datetimeを文字列にしたもの->Dateまたは文字列の変換には、dateformat.js - 日付フォーマット変換ライブラリを使った。おかげでかなり楽ができた。JSONにレンダリングするときに、dateフィルタで変換しても良かったかな。

var __dateFormat = new DateFormat("yyyy/MM/dd HH:mm:ss");
var __dateSequential = new DateFormat("yyyyMMddHHmmssSSS");
var __fromPython = new DateFormat("yyyy-MM-dd HH:mm:ss.SSS000");

/*
 * python形式の日時文字列(yyyy-MM-dd HH:mm:ss.SSS000)をDateオブジェクトに変換します。
 * @param datestr python形式の日時文字列
 */
_pydate2date(datestr) {
  if (!datestr) { return ''; }
    return __fromPython.parse(datestr);
  }
}
/*
 * python形式の日時文字列(yyyy-MM-dd HH:mm:ss.SSS000)をyyyy/MM/dd HH:mm:ss形式に変換します。
 * @param datestr python形式の日時文字列
 */
_pydate2str(datestr) {
  if (!datestr) { return ''; }
  return __dateFormat.format(_pydate2date(datestr));
}
/*
 * python形式の日時文字列(yyyy-MM-dd HH:mm:ss.SSS000)をyyyyMMddHHmmssSSS形式に変換します。
 * @param datestr python形式の日時文字列
 */
_pydate2Sequential(datestr) {
  if (!datestr) { return ''; }
  return __dateSequential.format(_pydate2date(datestr));
}

1000マイクロ秒以下に値が入ることはない、という前提で書いてるけど、あってるのかな?