GAE Tips Vol.1

そんな訳で、GAEいじり始めたので、恒例のTipsを書いていきます。
要するに、僕が何か調べたことのまとめですね。
普通、調べたってことはそれなりに有益な情報になるはずなんですが、僕みたいな人類もどき程度の視点から調べるという前提において、それは非常識の部類に区分されるようです。
つまり。
いつも通り、ふわふわした記録になるでしょう。


とりあえず、書こうと思っている内容は本家のチュートリアルに沿って一通り実行していった時に思ったことなんかが中心になります。
加えて言うなら、静的に書かれたHTMLファイルの扱いなんかも合わせてかくつもりです。まあ、この辺は単にCSSに関する扱いの応用なんですが。
そんなことに気づかずに四苦八苦していた僕が『単に』と言うのもなんですが。
人生に近道はないという良い教訓になりました。
しかし、その場合ゴールはどこになるんでしょうね。
目的地によっては、近道が回り道になってしまうことも十二分にあり得ます。
近道だと思ってたのに、実際は全然近道でも通り道ですらもなくて、永遠にたどり着けないということも、これまたありがちな事象です。
ほんと。
僕は迷い牛にでも憑かれたんでしょうかね。


置いといて。
それでは、今回の内容ですが、

  • 普通に書かれたHTMLファイルの読み込み

のみです。
ええ、まだ全然なんですよ。
まあ、その辺はよしとして。
始めます。

普通に書かれたHTMLファイルの読み込み:

Googleチュートリアルでは、テンプレートを用いたHTMLの例が掲載されています。
でも、既に書かれたHTMLを使いたい場合もある訳です。
実は、その後に書かれたCSSファイルの扱い方を応用すれば簡単にできるんですが、それに気づくまでに僕は結構時間かかっちゃいました。
そういう訳で、一応メモです。


詳しくはチュートリアルCSSの扱い方に譲るとして、静的なファイル、つまりCGIなどで動的に作るのではないHTMLファイルなどは、プロジェクトのトップディレクトリに置いても見つけてくれませんでした。
たぶん、app.yamlでファイルの所在を明言する、あるいはディレクトリを指定しないと見つけてくれないんでしょう。裏を返せば、存在していても明言しなければないことになるということです。この辺は、また後でも触れます。


で。
チュートリアルにならい、まず以下のようなディレクトリ構成にしました。プロジェクトディレクトリ(僕はそう呼んでます)名は、testです。

test/ 
 |-- index.py
 |-- app.yaml
 |-- html-files/ 
      |-- index.html

で、各ファイルの中身ですが、index.pyは、

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/html'
    self.response.out.write('<a href="/html/index.html">test</a>')

application = webapp.WSGIApplication([('/', MainPage)],debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

次に、app.yaml

application: test
version: 1
runtime: python
api_version: 1

handlers:
-  url: /html
   static_dir: html-files

-  url: /.*
   script: index.py

そして、index.html、

<html>
<body>
Good bye, world.
</body>
</html>

簡単な例ということで、内容は簡素なものです。
試して頂いた上でコードの内容を見ていただければ、尚わかりやすいと思います。


ポイントは以下の2点です。

  • 静的なファイルは、置く場所を作っておく。
  • app.yamlのurlはstatic_dirのエイリアスのようなもの。

この内、2点目に関しては、特にtomcatなどを使ったことがある方は経験があると思いますが、要するにローカルのディレクトリにおけるファイルの位置とウェブ上のURLにおけるアドレスとを対応づけているという感じです。
言葉足らずで申し訳ないですが。
だから、上下で文字が異なるんです。逆に、違っていても構わないんです。app.yamlで対応付けさえ、きちんとなされていれば大丈夫です。
加えて、前述のファイルを見つけてくれない云々の件ですが、今トップのtestディレクトリにあるファイルはapp.yamlにおいてindex.pyのみです。だから、ここにindex.htmlを置いてリンク張ろうとしても、index.htmlを見つけてはくれないのです。
app.yamlは重要です。

2010/12/24 追記:

よくよく考えてみると、チュートリアルにおけるindex.htmlは認識されている訳ですから、存在しないことにはならないということです。
この辺は、まだ要検証ということで「嘘かいてんじゃねぇ!」とか突っ込まないでください。
おそらく、チュートリアルにおける絶対パス指定が鍵になってくるのだと思いますが、それもやりようがある気がしてならないので、色々検証した上で改めて訂正させていただきたいと思います。
仮にここを参照している方がいらっしゃいましたら、ご迷惑おかけしてすみませんでした。
まあ、いちいちこうして謝ってたら、僕はこの日記における全ての記事で謝罪しなければならなくなりそうですが。

追記 Part2:

やっぱり、静的なHTMLファイルは置き場所を作った方が楽だとおもいます。絶対パスなら行けるとか思ったんですが、面倒過ぎて検証もそこそこしかしてませんが……。
何かご存知の方、ご一報くださるとありがたいです。

結論:

実際にチュートリアルを進めると、helloworld.pyは、

import cgi

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
import os
from google.appengine.ext.webapp import template

class Greeting(db.Model):
  author = db.UserProperty()
  content = db.StringProperty(multiline=True)
  date = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
  def get(self):
    greetings_query = Greeting.all().order('-date')
    greetings = greetings_query.fetch(10)

    if users.get_current_user():
      url = users.create_logout_url(self.request.uri)
      url_linktext = 'Logout'
    else:
      url = users.create_login_url(self.request.uri)
      url_linktext = 'Login'

    template_values = {
      'greetings': greetings,
      'url': url,
      'url_linktext': url_linktext,
      }

    path = os.path.join(os.path.dirname(__file__), 'index.html')
    self.response.out.write(template.render(path, template_values))

class Guestbook(webapp.RequestHandler):
  def post(self):
    greeting = Greeting()

    if users.get_current_user():
      greeting.author = users.get_current_user()

    greeting.content = self.request.get('content')
    greeting.put()
    self.redirect('/')

application = webapp.WSGIApplication(
                                     [('/', MainPage),
                                      ('/sign', Guestbook)],
                                     debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

こうなるというのが、当初分からなかった。
バカでごめんなさい……。