jquery mobileのレンダリングではまった

*この記事はjquery mobile のバージョンalpha 3に基づいて書いています。
2009年頃にGoogle App Engineを利用して個人的に使うために作ったライフログ「なにした?」

Archive for the ‘Google App Engine’ Category
http://blog.nal-6295.biz/?cat=128

をjquery mobile対応すべくやり始めていたのだが、後から挿し込むデータのレンダリングではまった。
基本、HTMLだけ書いとけばいいよ的なjQuery mobileはレンダリングはどうも$(document).ready()なタイミングで行う仕様らしく、しかも後から任意のタイミングでレンダリングしなおすためのメソッドも提供されていないらしく、ちょっと考え方を変える必要がありそうだ。
さいわい、djangoのテンプレートの部分とPythonのロジックの部分が分離されているので、urlマッピングとテンプレートの部分を新しく書きなおすだけで良さそう。
ま、そのうちやりましょう。

FacebookTwitterHatenaTumblr共有

とりあえず、暫定的に日本時間で表示されるようにした。

google app engineは通常だと標準時間の表示になります。

つまり日本時間-9時間になります。

そこで、本当はtz_info等を利用してちゃんとするべきなのですが、暫定的に

PaymentInfo(支払い情報)モデルクラスにjapan_timeメソッドを追加し、djangoのテンプレート側でそれを呼んであげることで、PaymentInfo.dateの日付情報を+9時間して表示するようにしました。

まず最初にmodels.pyの該当部分ですが、コメントで解説しています。

class PaymentInfo(db.Model):
author = db.UserProperty()
title = db.StringProperty()
value = db.IntegerProperty()
date = db.DateTimeProperty(auto_now_add=True)
#ここで、japan_timeというメソッドを追加しておき、dateプロパティの値に9時間足しています。
def japan_time(self):
return self.date + datetime.timedelta(hours=9)

そして、django側の該当部分ですが、

{% for value in outputValues  %}
<tr>
{# value.japan_timeとすることで、PaymentInfoで定義したメソッドを呼ぶようになっています。#}
<td>{{ value.japan_time }}</td>
<td>{{ value.title|escape }}</td>
<td>{{ value.value }}</td>
</tr>
{% endfor %}

ちなみに、今までイメージを貼ってませんでしたが、お小遣い帳のイメージはこんな感じです。

f:id:NAL-6295:20080708012444p:image

お小遣い帳のテンプレートで出力値をエスケープしていなかったのでエスケープするようにした

通常djangoでパラメータを出力する時は

{{ parameter }}

としますが、パラメータの値中にあるhtmlにとっての特殊文字をエスケープして出力する場合(ver0.95まで)

{{ parameter | escape }}

といったようにescapeフィルタをつけてあげることで実現します。

このような仕様によって、コードからテンプレートにエスケープ処理を追い出す事が可能になっていて、結果的に複雑度を分散しそれぞれがシンプルになっています。

また、出力仕様は出力するところに書くことで、出力仕様に関連するスコープも狭くする事ができました。

余計な話ですが、開発版である0.97preでは、自動エスケープがデフォルトの設定で

{{ parameter }}

の状態でエスケープが行われます。

これを解除(つまりエスケープしないようにする)場合は明示的に値が安全であることを示すために

{{ parameter | safe }}

safeフィルタをつけてあげる必要があります。

私もASP.NETでカスタムコントロールを利用して似たような事をしていたので、こちらの仕様に変わる事については歓迎です。

というわけで、djangoのテンプレートの部分だけ再掲

{{ parameter }}

となっていた部分のうち数値以外が入る部分について

{{ parameter | escape }}

としてあります。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="ja" xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{ userName|escape }}のお小遣い帳</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link href="./styles/styles.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="./scripts/util.js" ></script>
<script type="text/javascript" src="./scripts/index.js" ></script>
</head>
<body>
<div id="body">
<div id="header">
<div id="user">
お小遣い帳
</div>
<div id="navigationOperation" >■</div>
</div>
<div id="navigation" >
<div id="logonControl">
{{ userName|escape }} <br />
<a href="{{ logoutUrl }}">ログオフ</a>
</div>
<div id="settings">
<form action="/" method="post">
<div>チャージ額:<br /><input type="text" name="chargedValue" value="" tabindex="1" accesskey="1" /><br /></div>
<div>前回チャージ後総額:<br />{{ setting.properValue }}円<br /></div>
<div>総利用額:<br />{{ setting.totalPayment }}円<br /></div>
<div>残額:<br />{{ currentValue }}円<br /></div>
<div><input type="submit" value="チャージ"  tabindex="2" accesskey="s" /></div>
{% if settingErrorMessage %}
<div class="errorMessage">{{ settingErrorMessage|escape }} </div>
{% endif %}
</form>
</div>
</div>
<div id="contents">
<form action="/" method="post">
<div>使った内容:<select name="titles" tabindex="3">
<option value="" selected="selected"></option>
{% for value in titles %}
<option value="{{ value }}" >{{ value|escape }}</option>
{% endfor %}
</select><input type="text" name="title" value="" tabindex="4" accesskey="c" /></div>
<div>使った金額:<input type="text" name="payment" value="" tabindex="5" accesskey="3" /><br /></div>
<div><input type="submit" value="設定"  tabindex="6" accesskey="s" /></div>
{% if paymentErrorMessage %}
<div class="errorMessage">{{ paymentErrorMessage|escape }} </div>
{% endif %}
</form>
<div id="list" >
過去20件の利用状況:
<table border="1" cellspacing="0" >
<tr>
<th>日付</th>
<th>使った内容</th>
<th>金額</th>
</tr>
{% for value in outputValues  %}
<tr>
<td>{{ value.date }}</td>
<td>{{ value.title|escape }}</td>
<td>{{ value.value }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div id="footer">ご利用は計画的に。</div>
</div>
</body>
</html>

フォーム入力した値の検証について気づいた事

ずっと、GETのリクエストハンドラとPOSTのリクエストハンドラで違うルーティングをするものだと思っていたので、POSTで検証が通らなかったときにどうやって、その情報を渡しつつ元のフォームに戻るんだろうか。

という疑問があったが、form validation frameworkのサンプルページを読んでいたら、どうもGETもPOSTも同じリクエストハンドラで処理するみたいだ。

だったら、話は簡単だと思うわけで、特にform validation frameworkを利用しなくても良くて、ただ

class ****#リクエストハンドラ
def get(self):
  初期表示
def post(self):
#入力値検証
#検証が通らなかったらgetのレンダリング+エラーメッセージ
#通れば、post処理をした後、getのレンダリング

といった感じでも良さそう。でも、後学の為にもframeworkを使ってみても良さそう。

そういえば、ASP.NET MVC Previe2でも同じサンプルアプリケーションを作成する事で、構成の違いが載せられたら良いなぁ。

まだ良いなぁ。でしかないが。

間に合えば、CLTで使えたら、面白いかも?

Google App EngineとVisual Studio2008+MVC Preview2を使ったネタは許されないかも?