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
pythonとjavascriptで、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マイクロ秒以下に値が入ることはない、という前提で書いてるけど、あってるのかな?