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

ずっと、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を使ったネタは許されないかも?

Share

私のブログのエントリにおけるスタンスを自分なりに考えてみた

基本的に、思想カテゴリやコーディングカテゴリでは、どのプラットフォームにも適用できるような抽象的な事ばかりを狙って書いてきたつもり。

それゆえに即テクノロジとつながっているわけではないので、テクノロジに興味のある層へのリーチは薄いかも。

あとは、抽象的な事って気づいてしまえば結構単純なので、ネタ切れになりやすい。(特に貯蓄しているわけではないが。)

まぁ、最近は比較的拘らないで、C#やVBの新しい構文やgoogle app engineなどの.NET以外の話題も書いているんだけど(つっても、ここ1週間だけど)。

できるだけ、基本的な部分にとどめて、結局はほかの言語に読み替えても使えるぜ的な、つまり抽象的に解釈できるような記事が多いと思っている。

まぁ、でも正直なところ「何にも拘らない」「良いものは良い。」というのが私のスタンスなので、そんな感じで気づいた事をボンボン書いていこうかと。

そういえば、最近、「ナベアツ問題」で検索してもヒットしなくなりました。

零細ブログだけに。

まぁ、でも、どうでもいい事。

コードの10戒は、初稿どころか草案くらいのものなので、ちゃんと改訂して仕事に使いたい。

最近、がちがちのコーディング標準に関しては、フレームワーク+コードの10戒レベルのルールでリプレースできるのではないかと思っています。

この件については、また別の機会にエントリできたら良いなぁ。

Share

formに入力した値の検証の仕方について

MVCの仕組みになれていなかったのでformに入力した値の検証の仕方が分からなかったが、DJangoのform validation frameworkという機能を利用するらしい。

後で使うので、説明しているページをメモ。

Introduction

In this article we show how to use Django’s form validation framework with the Google App Engine. This framework allows you to construct HTML forms from your data models, and handle the inputted information from the forms seamlessly when interacting with the datastore.

The Django Form Validation Framework on Google App Engine

Share

DJangoのテンプレートを利用したサンプルアプリの作成

最初に、サンプルアプリの仕様を説明

概要:
・1からインクリメントしながら番号を表示し、3の倍数と3の付く数字のときに別の文字列を、5の倍数のときにも更に別の文字列を表示する。
・終点となる番号が設定できる。
・3の倍数と3のつく数字のときに読み上げる文字列を設定できる。
・5の倍数のときに読み上げる文字列を設定できる。
・設定した値はユーザ毎に永続化される。
・アプリケーション名は世界の○○とする。○○のところはユーザのnicknameを設定したい。
・formのvalidationについては、今回は行わない。(本当は行う必要があるので、次回以降のエントリで機能追加します。

最終的に作成したい画面イメージ

f:id:NAL-6295:20080516130415j:image

それではユーザ毎の設定値を保持するためのモデルを定義したいと思う。

ユーザ毎の設定値なのでとりあえずクラス名は、nabeatsuUserMasterとし、db.Modelを継承する。

継承することで、Google App EngineのDataStoreを利用して永続化するためのモデルと認識される。

あとは、サンプルのコメントで説明する。

#の行がコメント。

青色になっていると思う。

#DataStoreの機能を利用するためにimportする。
from google.appengine.ext import db
#db.Modelを継承することで、永続化するためのモデルとして認識される。
#RDBのテーブルのようなものです。
#項目は項目名 = db.型Property(パラメータ)で定義される。
class nabeatsuUserMaster(db.Model):
#この設定のユーザを識別するための項目でUser型の項目にします。
 #GoogleにログインしているUser情報が保存される。
author = db.UserProperty()
#終点となる番号をInteger型の項目とします。
count = db.IntegerProperty()
#とりあえず、利用しませんが、このデータの更新日時をDateTime型の項目とします。
date = db.DateTimeProperty(auto_now_add=True)
 #3の倍数と3のつく数字の時に読み上げたい文字列をString型の項目とします。
saidWhen3 = db.StringProperty()
 #5の倍数の時に読み上げたい文字列をString型の項目とします。
saidWhen5 = db.StringProperty()

これで、とりあえず、nabeatsuUserMasterは完成です。

つづいて設定と読み上げを行うためのページを定義します。

ここで、DJangoのテンプレートを作成します。ファイル名は、なんでも良いのですが、とりあえずindex.htmlとします。

基本的な機能だけを利用していますので先に利用している機能を説明します。

{{ parameterName }}

で後で説明するwebのリクエストをハンドラで定義したパラメータを出力します。

{% 構文 %}

とすることである程度の構文が使えます。今回はfor value in valuesのみ利用していますので、

valuesからvalueを取り出して出力という処理を

{% for value in values %}

{{ value }}

{% endfor %}

といった感じで実装しています。

後で説明するリクエストハンドラから受け付けているパラメータ名の説明をしておきます。

userName=今ログインしているユーザのnickname

logoutUrl=ログアウトするためのUrlです。

count=読み上げる数字の終点です。

saidWhen3=3の倍数と3のつく数字のときに表示する文字列

saidWhen5=5の倍数のときに表示する文字列

outputValues=読み上げた文字列のリストです。

index.htmlの内容です。

<?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 }}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="/nabeatsu/styles/styles.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="header">
世界の{{ userName }} <a href="{{ logoutUrl }}">ログオフ</a>
</div>
<form action="/nabeatsu/set" method="post">
<div>読み上げてほしい数:<input type="text" name="count" value="{{ count }}" tabindex="1" accesskey="c" /></div>
<div>3の倍数と3のつく数字のとき<input type="text" name="saidWhen3" value="{{ saidWhen3 }}" tabindex="2" accesskey="3" />になって、</div>
<div>5の倍数のとき<input type="text" name="saidWhen5" value="{{ saidWhen5 }}" tabindex="3" accesskey="5" />になります。</div>
<div><input type="submit" value="設定"  tabindex="4" accesskey="s" /></div>
</form>
<div id="body" >
{% for value in outputValues  %}
{{ value }} <br />
{% endfor %}
</div>
</body>
</html>

つづいて設定を更新するためのリクエストハンドラを作成します。

ここで、前もって作成しておいたnabeatsuUserMasterにデータを追加・更新する部分の作成します。

前段のテンプレートどおり、/nabeatsu/setに対応したハンドラとして、webapp.RequestHandlerを継承してUserMasterUpdateというクラスを作成します。

今回は、nabeatsuUserMasterと1対1で対応していますので、同じスクリプトファイルに記述されている前提です。

例によってサンプル中のコメントで説明します。

#dataStoreを利用するためのインポート
from google.appengine.ext import db
#web applicationを作成するための機能をりようするためのインポート
from google.appengine.ext import webapp
#googleのユーザ機能を利用するためのインポート
from google.appengine.api import users
#nabeatsuUserMasterクラスは前のセクションで説明したものですので、読み飛ばしてください。
class nabeatsuUserMaster(db.Model):
author = db.UserProperty()
count = db.IntegerProperty()
date = db.DateTimeProperty(auto_now_add=True)
saidWhen3 = db.StringProperty()
saidWhen5 = db.StringProperty()
#webapp.RequestHandlerを継承して作成することで、リクエストハンドラとしての機能を持ちます。
class UserMasterUpdate(webapp.RequestHandler):
#今回は、データの設定なのでpostメソッドを作成します。HTTP POSTに対応しています。
def post(self):
#現在ログオン中のユーザを取得します。
user = users.get_current_user()
#ユーザが存在しない場合、users.create_login_urlメソッドを利用して、ログインページにリダイレクトします。
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
#index.htmlのテキストボックスcountの値が入力されていなかったら、何もしないで元のページにリダイレクトします。
#今回は、form validationを行わないので、この程度にしておきます。
if self.request.get('count').count == 0:
self.redirect('/nabeatsu/')
return
#nabeatsuUserMasterに対象ユーザのユーザ情報を検索条件として、検索します。
#GQLというクエリに良く似たものを利用しています。
Counts = nabeatsuUserMaster.gql("where author = :author",author=user)
#取得したインスタンスリストの件数が0件の場合は、新しいnabeatsuUserMasterインスタンスを作成します。
#そうで無い場合は、取得したインスタンスリストの先頭を取得します。
if Counts.count() <= 0:
Count = nabeatsuUserMaster()
else:
Count = Counts[0]
#各種フォームデータをインスタンスに設定します。
Count.author = user
Count.count = int(self.request.get('count'))
Count.saidWhen3 = self.request.get('saidWhen3')
Count.saidWhen5 = self.request.get('saidWhen5')
#putでdataStoreに永続化されます。
Count.put()
#設定が終わったところで最初のページにリダイレクトして終了です。
self.redirect('/nabeatsu/')

つづいて、一番肝心なindex.htmlに対応したリクエストハンドラを作成します。

ここで、テンプレートであるindex.htmlに対応したリクエストハンドラを作成します。

このリクエストハンドラ内で、該当ユーザ毎のデータを持ってきて、読み上げた数字のリストを他のデータとともに、テンプレートに渡しています。

例によってサンプル内のコメントで説明します。

# -*- coding: utf-8 -*-
import wsgiref.handlers
import cgi
import os
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.api import users
#テンプレート機能をつかうために意ポート
from google.appengine.ext.webapp import template
#先ほど定義した、nabeatuUserMasterとそれを更新するためのリクエストハンドラをインポート
import masters
#MainPageという名前でwebapp.RequestHandlerを継承して実装します。
class MainPage(webapp.RequestHandler):
#数字を読み上げるロジックを実装しています。
def nabeatsu(self,saidWhen3,saidWhen5):
def reader():
isAho = reader.value % 3 == 0 or str(reader.value).find('3') >= 0
isDog = reader.value % 5 == 0
outputValue = ''
if isAho:
#引数で渡されたsaidWhen3をUTF-8にエンコードした上で、有害な文字列が実行されないようにエスケープ処理
outputValue += cgi.escape(saidWhen3.encode('UTF-8'))
if isDog:
#引数で渡されたsaidWhen5をUTF-8にエンコードした上で、有害な文字列が実行されないようにエスケープ処理
outputValue += cgi.escape(saidWhen5.encode('UTF-8'))
if not isAho and not isDog:
outputValue = cgi.escape(str(reader.value))
reader.value += 1
return outputValue
reader.value= 1
return reader
#HTTP GETに対応した、メソッドを実装します。
def get(self):
#現在のユーザ情報を取得します。
user = users.get_current_user()
#ログインしていなければ、ログインページにリダイレクトして終了します。
if not user:
self.redirect(users.create_login_url(self.request.uri))
return
#各種初期値を設定しておきます。
#読み上げる数字の初期値は40で、3の倍数と3のつく数字のときは'あほ!'になって、5の倍数のときは'わん!'となるのがデフォルトです。
count = 40
saidWhen3 = u'あほ!'
saidWhen5 = u'わん!'
#自分自身の設定データをクエリで取得します。
currentCount = masters.nabeatsuUserMaster.gql("where author = :author",author=user)
#データが存在してれば、各種データを設定します。
if currentCount.count() > 0:
count = currentCount[0].count
saidWhen3 = currentCount[0].saidWhen3
saidWhen5 = currentCount[0].saidWhen5
#nabeatsuメソッドに引数を渡して、readerメソッドのインスタンスを取得します。
reader = self.nabeatsu(saidWhen3,saidWhen5)
#出力データリストを初期化します。
outputValues = []
#設定された数字まで、reader()を実行し続けます。
for i in xrange(1,count + 1,1):
#戻り値が出力する文字列になっていますので、outputValuesに対してappendで追加していきます。
outputValues.append(reader())
#ここでテンプレートに渡すパラメータ群を設定します。
#logoutUrlをcgi.escapeしているのは、url中に&が出現しているから。
template_values = {
'userName' : user.nickname(),
'logoutUrl': cgi.escape(users.create_logout_url("/nabeatsu/")),
'count' : count,
'saidWhen3' : saidWhen3,
'saidWhen5' : saidWhen5,
'outputValues': outputValues,
}
#このスクリプトとテンプレートが同じフォルダにおいてあるので、テンプレートの完全なパスを作成します。
path = os.path.join(os.path.dirname(__file__), 'index.html')
#template.renderにテンプレートとテンプレートに渡すパラメータ群を設定することで、実際のhtmlが出力されます。
self.response.out.write(template.render(path, template_values))
#最初に実行されるmainメソッドです。
def main():
#ここで、パスとリクエストハンドラを紐付けています。debug=trueの間はエラーのときに、tracebackが表示されます。
application = webapp.WSGIApplication(
[('/nabeatsu/', MainPage),
('/nabeatsu/set', masters.UserMasterUpdate)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()

最後にGoogle App Engineのアプリケーション定義をして終了です

上から、

アプリケーションの名前

バージョン

ランタイム(pythonしかないので固定)

apiのバージョン(いまのところ1)

handlersはurlとスクリプトの割り当て

/nabeatsu/とnabeatsu.pyを割り当て

/nabeatsu/setとnabeatsu.pyを割り当て

スタイルシートのパスを割り当て

application: nabeatsu
version: 1
runtime: python
api_version: 1
handlers:
- url: /nabeatsu/
script: nabeatsu.py
- url: /nabeatsu/set
script: nabeatsu.py
- url: /nabeatsu/styles/
static_dir: styles

以上で、完成です。

スタイルシートについては適当に設定してあげれば良いですが、一応サンプルとして添付しておきます。

#body
{
color:blue;
font-weight:bold;
height:200px;
overflow-y:scroll;
border:solid 1px black;
}
body
{
color:Black;
margin: auto;
FONT-FAMILY: "MS ゴシック";
FONT-SIZE: 9pt;
FONT-WEIGHT: normal;
LETTER-SPACING: normal;
TEXT-TRANSFORM: none;
background-color: #FFFFFF;
WORD-SPACING: normal;
}

とりあえず、簡単なサンプルですが、用意した機能の基本的な部分を利用しながら作成する手順については追えるのではないでしょうか。

Share

DataStoreが手軽でいい

インフラを気にしないで良いという事が、これほど楽ちんなのかという感じ。

いちいち、接続先を指定しなくても良くて、インスタンスを生成してPutすればそれがInsertになるし、GQLでインスタンスのリストを取得して変更してPutすればUpdateになるし、Delete()すれば削除になる。

超簡単。

エンティティの定義も

from google.appengine.ext import db
class nabeatsuUserMaster(db.Model):
#ナベアツを利用するユーザのマスタ
author = db.UserProperty()
count = db.IntegerProperty()
date = db.DateTimeProperty(auto_now_add=True)
saidWhen3 = db.StringProperty()
saidWhen5 = db.StringProperty()

ってdb.Modelを継承したクラスを作成するだけで良い。

また、GQLってのがSQLに似ているんだけど、エンティティのプロパティの一部だけ持ってくるという事はできなくて、常に

  #パラメタライズドクエリみたいな感じで:authorを定義しておいて、author=userで割り当てを行う。
  #これもいろんなやり方があって、必ずしも名前が必要というわけではない。
datalist = nabeatsuUserMaster.gql("where author = :author",author=user)
または、
datalist = db.GqlQuery("select * from nabeatsuUserMaster where author = :author",author=user)

と言った風にちゃんと全項目指定でnabeatsuUserMaster型として持ってくる必要がある。

個人的には、前者が好み。

DataStoreの詳細については、http://code.google.com/appengine/docs/datastore/を読むといいです。

比較的サンプルが多いし説明が平易なので、英語が分からなくても、なんとなくイケル感じ。

Share

ユーザ管理どころかログオンページを自前で実装する必要が無いのは楽

from google.appengine.api import users

ってインポートしておいてやれば、

user = users.get_current_user()
if user:
print 'ログイン済み'
else:
print 'ログインしてない。'

と判断できるし、ログインしていないときはハンドラの中の実装しようとしているgetとかpostメソッド内で

self.redirect(users.create_login_url(self.request.uri))

とやってやれば、勝手にログインページが作られるので楽だ。

self.response.out.write("<a href="%s">ログアウト</a>" %
(users.create_logout_url(self.request.uri)))

としておけば、ログアウトするためのリンクもできるし。

その部分に気を使う必要が無い。

DataStoreにも、ちゃんとユーザ情報を登録するためのUserProperty型が用意されているので、データ管理も楽。

持ってこれるユーザ情報もnicknameとemailアドレスだけというのも個人的には嬉しい。

Share

Visual Studio 2005 extensions for Windows SharePoint Services 3.0, version 1.1リリース

1ヶ月前の話ですが・・・

長らくお待たせいたしました、いよいよ日本語版のVSeWSSがご利用いただけるようになりました。

Visual Studio 2005 extensions for Windows SharePoint Services 3.0, version 1.1

http://www.microsoft.com/downloads/details.aspx?FamilyID=3E1DCCCD-1CCA-433A-BB4D-97B96BF7AB63&displaylang=ja

すでにバージョン1.0あるいはバージョン1.1のCTPをご利用いただいている方は、アンインストールしてからバージョン1.1をインストールしてください。

また、バージョン1.1からはVSとVSeWSSの言語を揃えないと正しく動作しません。日本語版のVSeWSSをご利用いただく場合は、日本語版のVSがインストールされている環境へインストールしてください。

SharePoint Developer's Blog : VSeWSS Version 1.1 日本語版 リリースのご案内

Share
カテゴリー: WSS

それをリストラと呼ぶのですよ

山下氏に続けて、代表取締役常務執行役員 榎本隆氏は「出向政策はリストラではなく構造改革」と説明。「転籍対象の98%の社員が理解をしてくれたことに感謝している」と述べた。

livedoor ニュース – NTTデータ社長 1番ほっとしたのは社員の転籍問題

構造改革の事をリストラと言うんですよ代表取締役。

その発言「豆腐チゲはチゲではなく鍋」みたいなもんですよ。

会社の首脳なのだから、そのあたりはキチッとしておいたほうが・・・。

Share