HOME > ブログ > ブログをGoogle App Engineへ移行してみる11

ブログをGoogle App Engineへ移行してみる11

各記事にコメントを投稿できるようにしていきます。






現在の状態は記事一覧で全て情報が表示されていますが、これを記事タイトルをクリックしてその記事の詳細を表示する形にします。
で、その記事詳細画面にコメントを投稿するフォームを設けることにします。

/main.py
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import os
from google.appengine.ext.webapp import template
from google.appengine.ext import db
import datetime
import admin
from model import Entry

class EntryList(webapp.RequestHandler):
    def get(self):
        entries = Entry.all().order('-created_at')
        template_values = {
            'entries': entries
        }
        path = os.path.join(os.path.dirname(__file__), 'template/entries/index.html')
        self.response.out.write(template.render(path, template_values))

class EntryShow(webapp.RequestHandler):
    def get(self, key):
        entry = db.get(db.Key(key))
        template_values = {
            'entry': entry
        }
        path = os.path.join(os.path.dirname(__file__), 'template/entries/show.html')
        self.response.out.write(template.render(path, template_values))

class CommentNew(webapp.RequestHandler):
    def post(self, key):
        self.redirect("/entry/show/"+key)

application = webapp.WSGIApplication([
                                     ('/', EntryList),
                                     ('/entry/show/([\w\-]+)', EntryShow),
                                     ('/comment/new/([\w\-]+)', CommentNew),
                                     ('/admin/', admin.EntryList),
                                     ('/admin/new', admin.EntryNew),
                                     ('/admin/edit/([\w\-]+)', admin.EntryEdit),
                                     ('/admin/delete/([\w\-]+)', admin.EntryDelete)
                                     ],debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

/template/entries/index.html
{% extends "../layout.html" %}

{% block script_function %}{% endblock %}

{% block h1 %}投稿一覧{% endblock %}

{% block contents %}
{% for entry in entries %}
  <h2><a href="/entry/show/{{ entry.key }}">{{ entry.title|escape }}</a></h2>
  <p>{{ entry.jst_created_at|date:"Y年m月d日 H時i分" }}</p>
  <hr />
{% endfor %}
{% endblock %}

/template/entries/show.html
{% extends "../layout.html" %}

{% block script_function %}{% endblock %}

{% block h1 %}記事詳細{% endblock %}

{% block contents %}
  <h2>{{ entry.title|escape }}</h2>
  <p>{{ entry.jst_created_at|date:"Y年m月d日 H時i分" }}</p>
  <p>{{ entry.body|escape }}</p>
  
<form action="/comment/new/{{ entry.key }}" method="post">
コメント
<textarea name="comment" cols="40" rows="5"></textarea>
<input type="submit" value="コメントを投稿する" />
</form>
  <a href="/">記事一覧に戻る</a>
{% endblock %}



こんな感じになります。
ソースを見てもらえばわかるとおり「コメントを投稿する」ボタンを押すと何も処理をせずに元の記事詳細にリダイレクトしています。


画面の遷移はこれでOKだと思いますので、処理部を作っていきます。
まずはコメントのモデルを考えましょう。
いち記事に対してコメントは複数つきますのでモデルは1対多の関係になりますね。
appengineで1対多を実現する手法は過去に当ブログで紹介しているので参照ください。

Google App Engineでone-to-many(1対多)2kindとReferenceProperty()で(2010/04/29)
Google App Engineでone-to-many(1対多)1kindとListProperty()で(2010/04/30)
Google App Engineでone-to-many(1対多)2kindとエンティティグループで(2010/05/01)

ここではどれを採用するべきでしょうか?
「ListProperty()」を使うのは無理がありそうなので親子関係を「ReferenceProperty()」を使って作るのか「parent」を使って作るのかのどちらがやりやすいかになりますね。好みです。
というか好みというほど作りまくっているわけではないので正直どちらがいいのかはわかりません。さらに言うなら上記の手法とは違うやり方で爆速な方法もあるらしいのでできるならそちらを採用したい。採用したいが少し時間をとって理解しないとできなさそうなので後からその手法に変えるとして、とりあえず「parent」を使ったエンティティグループで作ってみます。

特別なプロパテイを用意する必要はないので
class Comment(db.Model):
    comment = db.TextProperty()
    updated_at = db.DateTimeProperty(auto_now=True)
    created_at = db.DateTimeProperty(auto_now_add=True)
例によって他にいるであろう要素は後でいる時になって追加していきます。

過去記事を見返しながら(自分でやっておいてすっかり忘れている)処理部を実装してみます。

/model.py
from google.appengine.ext import db
import datetime

class Entry(db.Model):
    title = db.StringProperty()
    body = db.TextProperty()
    updated_at = db.DateTimeProperty(auto_now=True)
    created_at = db.DateTimeProperty(auto_now_add=True)

    def jst_created_at(self):
        return self.created_at + datetime.timedelta(hours=9)

    def comments(self):
        return Comment.all().ancestor(self).order('created_at')

class Comment(db.Model):
    comment = db.TextProperty()
    updated_at = db.DateTimeProperty(auto_now=True)
    created_at = db.DateTimeProperty(auto_now_add=True)

    def jst_created_at(self):
        return self.created_at + datetime.timedelta(hours=9)

/main.py
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import os
from google.appengine.ext.webapp import template
from google.appengine.ext import db
import datetime
import admin
from model import Entry, Comment

class EntryList(webapp.RequestHandler):
    def get(self):
        entries = Entry.all().order('-created_at')
        template_values = {
            'entries': entries
        }
        path = os.path.join(os.path.dirname(__file__), 'template/entries/index.html')
        self.response.out.write(template.render(path, template_values))

class EntryShow(webapp.RequestHandler):
    def get(self, key):
        entry = db.get(db.Key(key))
        template_values = {
            'entry': entry
        }
        path = os.path.join(os.path.dirname(__file__), 'template/entries/show.html')
        self.response.out.write(template.render(path, template_values))

class CommentNew(webapp.RequestHandler):
    def post(self, key):
        entry = db.get(db.Key(key))
        if entry:
            comment = Comment(parent=entry)
            comment.comment = self.request.get("comment")
            comment.put()
            self.redirect("/entry/show/"+key)
        else:
            self.redirect("/")

application = webapp.WSGIApplication([
                                     ('/', EntryList),
                                     ('/entry/show/([\w\-]+)', EntryShow),
                                     ('/comment/new/([\w\-]+)', CommentNew),
                                     ('/admin/', admin.EntryList),
                                     ('/admin/new', admin.EntryNew),
                                     ('/admin/edit/([\w\-]+)', admin.EntryEdit),
                                     ('/admin/delete/([\w\-]+)', admin.EntryDelete)
                                     ],debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()
ちゃんと投稿できたかどうかこの段階では画面上で確認できませんので「SDK Console」で確認します。

「parent」にデータが入っています。どうやらちゃんとデータは挿入されたようですね。

最後に投稿したコメントを記事詳細画面の記事の下に出します。

/template/entries/show.html
{% extends "../layout.html" %}

{% block script_function %}{% endblock %}

{% block h1 %}記事詳細{% endblock %}

{% block contents %}
  <h2>{{ entry.title|escape }}</h2>
  <p>{{ entry.jst_created_at|date:"Y年m月d日 H時i分" }}</p>
  <p>{{ entry.body|escape }}</p>
  <hr />
■コメント
{% for comment in entry.comments %}
  {{ comment.comment }}
  <hr />
{% endfor %}
  <form action="/comment/new/{{ entry.key }}" method="post">
  コメント
  <textarea name="comment" cols="40" rows="5"></textarea>
  <input type="submit" value="コメントを投稿する" />
  </form>
  <a href="/">記事一覧に戻る</a>
{% endblock %}
モデルのEntryクラスに所属コメント一覧をひっぱってくるメソッドを追加していますのでテンプレートから直接「entry.comments」でデータを持ってこれます。


出ました。
投稿していけばコメントが下にどんどん追加されていくことになります。




| Google App Engine | Comment:0 |
コメント投稿












画像リロード
*半角の小英字、数字で構成されています