Reactをつかってみる
http://coreblog.org/ats/moblog2005-12-13-18-25
atsさんに教えていただいた
http://timmorgan.org/wiki/ReactFrameworkForZope
Reactなる物を使ってみる。
RDBをマッパーするものでありRuby on Railsを意識して作ったようです(object-relational mapper)。
まず、Readmeなどをあさったり、Helpをみたが懇切丁寧とは言えないかな、というHelpであった。なのでサンプルとしてブックマークを作ってみました。
http://nakaj.net/testFirebird/react/home/index
まず、ReactをいつものパターンでZopeに追加。
Productフォルダに拾ってきたファイルを展開。
任意の場所にReact Application Folderをまず追加しますとcontrollers,helpers,models,public,viewsというフォルダ群ができあがります。またReact Application FolderのHelpがGetting start...になっておりますので、基本的なサンプルコードはここを参照してください。
#というか、このGetting startで理解できる人は以下を読む必要ないです。
まずmodelsフォルダにいきましょう。
そこにお好きなDAを追加してやります。わたしはFirebirdを使いたかったのでKInterbasdbDAを追加しました。
TableをFirebird上に作ります。
こんなTable
create table bookmarks( ID INTEGER Not Null, URL VARCHAR(255) Nullable, NAME VARCHAR(255) Nullable, DESCRIPTION VARCHAR(255) Nullable, Primary key (ID) );そしてReact Modelを追加します。
db_idは作ったTableの名前、bookmarksにします。TableはIDというカラムが必須でなおかつオートインクリメントがつかえるRDBMSなら、そうするのが楽なようです(Firebirdでもトリガーとジェネレータで同じような機能をつくれますが、ここでは行いません)。
そしてcontrollersフォルダへ移動し'home'という名前のパイソンスクリプトを作成します。
内容は以下のような感じとしました(笑わないでね)。
class home: layout = 'application' def index(self): self.bookmarks = models.bookmarks.find_all() def view(self): self.bookmark = models.bookmarks.find(params['id']) def update(self): bookmark = models.bookmarks.find(params['id']) self.bookmark = bookmark current_name = bookmark.NAME bookmark.id = params['id'] bookmark.URL = params['url'] bookmark.NAME = params['new_name'] bookmark.DESCRIPTION = params['description'] bookmark.save() page = 'view?id='+params['id'] self.pageRedirect(page) def new(self): tmp = models.bookmarks.find_by_sql("id = (select max(id) from bookmarks)") tid = tmp.ID +1 bookmark = models.bookmarks.new(ID=tid, URL=params['url'], NAME=params['new_name'], DESCRIPTION=params['description']) bookmark.save() page="index" self.pageRedirect(page) def destroy(self): bookmark = models.bookmarks.find(params['id']) bookmark.destroy(params['id']) page="index" self.pageRedirect(page) def pageRedirect(self,page): request = container.REQUEST RESPONSE = request.RESPONSE RESPONSE.redirect(page) return homedestroyは実は自分で改造してしまいました。本来サンプルに示されているとおり、
thing = models.things.find(123) thing.destroy()という上記のコードでレコード削除が可能なようですが、全くうまくいかなかったのでIDを指定することで削除可能にしてしまいました。
newのところで副問い合わせをつかっていますが、先に書いたようにオートインクリメントがつかえるDBMSならこれは必要なくなるでしょう。 また、本来データの保証が無いのでこういうことをSQL直発行はしちゃいけませんね。一応注記しておきます。
さて、こんどはviewsフォルダへ移動します(忙しいですね、こうやってフレームワークに縛られることによってMVCに沿ってアプリケーションを作りましょう、というプロダクトなんですね)。
そこにも先ほどつくった"home"に応対してやはりhomeというフォルダを作ります。なかにはこんどは上記のclass homeに対応したzptを追加していきます。
各メソッドごとにindex,view,update,new,destroyです。 このうちupdate,new,destroyは空のコードにしました(もっと賢い方法があれば教えてください)。
例えばindexはこんな内容
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body tal:define="results bookmarks; start request/start|python:0; batch python:modules['ZTUtils'].Batch(results, size=10, start=start); previous python:batch.previous; next python:batch.next"> <p> <a tal:condition="previous" tal:attributes="href string:${request/URL0}?start:int=${previous/first}" href="previous_url">previous <span tal:replace="previous/length">10</span> results</a> <a tal:condition="next" tal:attributes="href string:${request/URL0}?start:int=${next/first}" href="next_url">next <span tal:replace="next/length">10</span> results</a> </p> <table border> <tr> <th>ID</th> <th>NAME</th> <th>DESCRIPTION</th> </tr> <div tal:repeat="result batch" > <tr> <td><a tal:condition="result/ID" href="#" tal:attributes="href string:view?id=${result/ID}"> <span tal:replace="result/ID">ID goes here</span></a></td> <td><a tal:condition="result/URL" href="#" tal:attributes="href result/URL"> <span tal:replace="result/NAME">NAME goes here</span></a></td> <td><span tal:replace="result/DESCRIPTION">DESCRIPTION goes here</span></td> </tr> </div> </table> <p> <form action="new"> <label>URI :</label> <input type="text" size="60" name="url" /> <label>NAME:</label> <input type="text" size="60" name="new_name" /> <br /> <label>説明:</label> <input type="text" size="80" name="description" /> <br /> <input type="submit" value="追加"> </form> </p> <p> <a tal:condition="previous" tal:attributes="href string:${request/URL0}?start:int=${previous/first}" href="previous_url">previous <span tal:replace="previous/length">10</span> results</a> <a tal:condition="next" tal:attributes="href string:${request/URL0}?start:int=${next/first}" href="next_url">next <span tal:replace="next/length">10</span> results</a> </p> </body> </html>としておきます。
viewは
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body tal:define="result bookmark"> <form action="update"> <input type="hidden" name="id" tal:attributes="value result/ID" /> <label>URI :</label> <input type="text" size="60" name="url" tal:attributes="value result/URL" /><br /> <label>NAME:</label> <input type="text" size="60" name="new_name" tal:attributes="value result/NAME" /><br /> <label>説明:</label> <input type="text" size="80" name="description" tal:attributes="value result/DESCRIPTION" /> <br /> <input type="submit"> </form> <a href="index">BookMark一覧へ戻る</a> </body> </html>こんな感じ。
なやんだのはZPTを私はほとんど使ったことが無かった点。
Zopeパーフェクトガイドを開いたが「げ、ZPT解説なし、じゃん」、、、。
しょうがないので再びかなり悩んだがふとZ Search Interfaceを使えばいいじゃん、ということに気付きちょっと反則だが一度z sql methodを追加しZ Search Interfaceに助けていただいた。
もちろんそのままでは使えないので手を入れましたが、おかげでZPT嫌いをいくらか克服できました。 (食わず嫌いでした)
うわ、なんか疲れた。元気が出たらもうちょっと読めるように修正します。
#atsさんご紹介有難うございました。
以下は使った感想ですが、まずFirebirdはdiarectによってカラム名が大文字で返したり、小文字で返したりするので注意が必要。
こうやってMVCで縛られて開発するというのはたとえ自分自身しかつかわないアプリケーションでも非常に見通しが良くなってすばらしい。 適当に思いつくままに作って後悔することの多い私には有効。
SQLを隠蔽することでこんなに楽ができるとは思いもよらなかった。昔ながらにz sql methodでやるとクエリー送信用のページを作って、結果表示のページを作って、SQLメソッドを書いて、中間処理をpython scriptで、、、、みたいにとっちからってしまうのだが、ReactがコードはcontrollにHTMLを返すものはViewにと強制してくれるので見通しが確保できる、とわかった。