• ログインログイン
  • 新規登録新規登録

MENU

Google App Engine 上でDjangoを動かしてホームページを作る 第5回

連載Google App Engine 上でDjangoを動かしてホームページを作る

Google App Engine 上でDjangoを動かしてホームページを作る開発レシピです。無料でサーバーを利用できるGoogle App Engineを使い、DjangoというPython言語で書かれた枠組みを用いてホームページを作っていきます。

前回までのプログラミング学習コラムに続き5回目の勉強内容です。

前回までで、DjangoをGAE上で動かす環境設定はできるようになりました。既に、Djangoのプロジェクトがあって、それをGAEで動かしたい場合には、そのプロジェクトディレクトリに、testappフォルダから拡張子が.pyと.yamlのファイルを入れ、また五つのdjango-nonrelファイルを入れればいいのでした。

では、もともとあるGAEのプロジェクトをDjango-nonrelで動くようにするにはどうすればいいのかをやっていきましょう。

GAEには、組み込みでwebappというwebフレームワークがあります。webappを使ったサンプルプロジェクトをDjango-nonrel用に書き換えていくことをしたいと思いますが、今回は投稿をデータベースに保存していって、それを表示する掲示板のようなサンプルプロジェクトの解説をしたいと思います。

これまでにやってきたようにまずは、新しいディレクトリを作って、その中にapp.yamlファイルと、その中でurlに紐づけされたmain.pyなどの実行コードを書きます。

まずはいつも通り、はじめは簡単にhelloをprintするところからいきましょう。app.yamlを以下の通り

application: yutakatraining
version: 1
runtime: python27
api_version: 1
threadsafe: no

handlers:
- url: .*
script: main.py

main.pyを次のようにすると

#!/usr/bin/env python
#-*- coding: utf-8 -*-

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

class MainHandler(webapp.RequestHandler):
def get(self):
self.response.out.write('hello')

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

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

ブラウザーにはhelloと表示されます。

MyHandlerクラスはリクエストに対しての動作を記述してあります。12行目では、webappのクラスであるWSGIApplicationを呼び出してますね。見ての通りですが、空白「/」のアドレスに対して、MainHandlerクラスが呼び出されます。それを、applicationに格納して、mainの中でrun_wsgi_appメソッドを使うことで実行するわけです。Templateを使って、helloを出す場合は、main.pyを

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp.template import render
from google.appengine.ext.webapp.util import run_wsgi_app

class MainHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(render('main.html', {}))

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

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

3行目と8行目の”render(‘main.html’, {})”の所が変更した点です。templateを返すためのモジュールを導入しています。もちろん、main.htmlも書かないとだめですね。タグはなんでもいいですが、ここでは

<h1>hello</h1>

とでもしておきましょう。今度は、フォームを使って、内容を送信できるページを作ります。まずはmain.pyを次のようにします。

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os

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

class MainHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(render('main.html', {}))
def post(self):
self.response.out.write("posted!")

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

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

そして、main.htmlは次のようにします。

HelloWorld!

<hr />

<form action="" method="post" accept-charset="utf-8"><textarea name="content" rows="3" cols="60"></textarea><input type="submit" value="Sign Guestbook" />

</form>

さて、これを実行すると、

次のようになりました。適当に打ち込んでみると

こうなります。Getメソッドに対しては、MainHandler内のgetが、formのpostメソッドに対しては、postが実行されます。

今度はこのpostされた内容をデータベースに格納してそれを表示できるようにしてみましょう。そのためには、dbモジュールを使います。

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os

from google.appengine.ext import db, webapp
from google.appengine.ext.webapp.template import render
from google.appengine.ext.webapp.util import run_wsgi_app

class Greeting(db.Model):
content = db.TextProperty()

class MainHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(render('main.html', {}))
def post(self):
greeting = Greeting(self.request.get('content'))
greeting.put()
self.redirect('/')

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

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

さて、これを実行して、なにか打ち込んでクリックしても消えるだけでしょう。しかし、実は、裏でデータベースにちゃんと格納されています。Greetingクラスがありますが、そこで、contentという変数が宣言されています。ここでは文字列を入れますよ!と宣言していますが、後述するように、時間を格納したり、ユーザー情報を格納したりすることも可能です。

さて、格納した文字データを今度は取り出して、表示するコードを書き加えます。

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import os

from google.appengine.ext import db, webapp
from google.appengine.ext.webapp.template import render
from google.appengine.ext.webapp.util import run_wsgi_app

class Greeting(db.Model):
content = db.TextProperty()

class MainHandler(webapp.RequestHandler):
def get(self):
greetings = db.GqlQuery('SELECT * FROM Greeting')
values = {
'greetings': greetings
}
self.response.out.write(render('main.html', values))
def post(self):
greeting = Greeting(self.request.get('content'))
greeting.put()
self.redirect('/')

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

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

GqlクエリとはGoogleが開発したクエリですね。Greetingsという変数にGreetingというデータベースクラスに格納された値を代入して、それを辞書型にしてvaluesに格納しなおしています。それを引数にしてテンプレートに入れているんですね。

テンプレートは次のようにします。

HelloWorld!

<hr />

{% for gr in greetings %}
<div>{{gr.content}}</div>
{% endfor %}

<form action="" method="post" accept-charset="utf-8"><textarea name="content" rows="3" cols="60"></textarea><input type="submit" value="Sign Guestbook" />

</form>

{% %}や{{}}はDjango用のテンプレート言語です。条件分岐などの操作を{%%}でやり、{{}}の中身に、レンダーする値が入ります。これを実行して、文字を打ち込むと確かに表示されます。さて、Gqlを使わないでmemcacheを使う方法もあります。

greetings = memcache.get('greetings')

もちろん、この場合、greetingsというのはデータベースモデルではないです。よって、データベースから引き出してきて、greetingsに加えるという作業が必要になります。

あとは、投稿日時や、誰が投稿したのか、投稿する際にログインできる機能などもついているといいですね。それらを追加するとmain.pyは以下のようになります。

import os

from google.appengine.api import memcache, users
from google.appengine.ext import db, webapp
from google.appengine.ext.webapp.template import render
from google.appengine.ext.webapp.util import run_wsgi_app

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

class MainHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
greetings = memcache.get('greetings')
if not greetings:
greetings = Greeting.all().order('-date').fetch(10)
memcache.add('greetings', greetings)
context = {
'user': user,
'greetings': greetings,
'login': users.create_login_url(self.request.url),
'logout': users.create_logout_url(self.request.url),
}
tmpl = os.path.join(os.path.dirname(__file__), 'main.html')
self.response.out.write(render(tmpl, context))

class GuestBook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
greeting.content = self.request.get('content')
greeting.put()
memcache.delete('greetings')
self.redirect('/')

application = webapp.WSGIApplication([
( '/', MainHandler),
( '/sign', GuestBook),
], debug=True)

def main():
run_wsgi_app(application)

if __name__ == '__main__':
main()

今度は、Greetingクラスの中に、文字だけでなくて、db.UserProperty()やdb.DateTimePropertyなども追加されていますね。前者でアプリケーションにアクセスしたユーザー情報を後者で現在時刻を格納しています。

テンプレートは次のようにしましょう

Hello
{% if user %}
{{ user.nickname }}!
[<a href="{{ logout }}"><strong>sign out</strong></a>]
{% else %}
World!
[<a href="{{ login }}"><strong>sign in</strong></a>]
{% endif %}
<h2>Top 10 Most Recent Guestbook Entries</h2>
{% for greeting in greetings %}

<small>[<em>{{ greeting.date.ctime }}</em>]</small>
<strong>
{% if greeting.author %}
<code>{{ greeting.author.nickname }}</code>
{% else %}
<em>anonymous</em>
{% endif %}
</strong>
wrote:
{{ greeting.content|escape }}
{% endfor %}

<hr />

<form action="/sign" method="post" accept-charset="utf-8"><textarea name="content" rows="3" cols="60"></textarea><input type="submit" value="Sign Guestbook" />

</form>

うまくいきましたら次のようになるでしょう。投稿が新しいものほど上にきています

はじめの方のHelloと出力するだけのコードでは、特定のHttpリクエストに対して、MainHandlerだけが実行されましたが、今度のものでは、formactionでpost送信に対して、http://アドレス/signというリクエストが呼び出されるようにしています。そのリクエストに対しては、GuestBookクラスが実行されるというひも付けがされています。

さて、これでデータベースを使ったwebappアプリのサンプルができました。次回はこれをDjango-nonrelに実装できるように書き換えることをしてみたいと思います。

オススメ記事一覧

もっと見る
完全無料!

1で登録完了!

エンジニアの仕事・年収や選考ノウハウ記事が読めるほか、
会員にはプログラミング講習やES・面接対策などリアルな無料サポートも充実。
ここだけの求人情報も多数。

今すぐ新規会員登録
Page Top