TurboGearsでblog作ってみる(その2)
前後半では収まらんので、その2にします。
では、editBlogの内容だ。
@expose(template="blog.templates.editBlog") @identity.require(identity.not_anonymous()) def editBlog(self, blogId): blog = Blog.get(blogId) if blog.author != identity.current.user: raise identity.IdentityException() return dict(blog=blog)
主キーによるアクセスは、クラスメソッドgetを使用する。
宣言的なアクセスコントロールは新規作成時と同じだが、
今回は更新対象のBlogのauthorであるという条件が加わる。
メソッドに宣言することはできないので、blog取得後にプログラム的アクセスコントロールを行う。
editBlogテンプレートは以下のようにしておく。
<body> <form action="save" method="post"> <input type="hidden" name="blogId" value="${blog.id}"/> <table> <tr><td><label>Date:</label></td> <td><input name="date" value="${blog.date}"/></td></tr> <tr><td><label>Title:</label></td> <td><input name="title" value="${blog.title}"/></td></tr> <tr><td><label>Description:</label></td> <td><textarea name="description">${blog.description}</textarea></td></tr> </table> <input type="submit" value="Update"/> </form> </body>
${...}内はpython式を書くことができる。
上では、コンテキスト中のblogオブジェクトにアクセスして、各プロパティを取得している。
mypageのEditリンクをたどると、そのblogオブジェクトの更新フォームに遷移する。
この状態のURLをどこかに記録しておこう。
再度tg-admin shellからユーザーを追加してそのユーザーでログインする。
さきほど記録しておいたURLにアクセスしてみよう。
更新フォームにはアクセスできず、ログインフォームが表示される。
ここでblogを作成したユーザーでログインすると、更新フォームが表示される。
実際の更新はsaveメソッドを利用する。
@expose() @identity.require(identity.not_anonymous()) def save(self, date, title, description, blogId=None): hub.begin() try: if blogId is None: b = Blog(date=date, title=title, description=description, authorID=identity.current.user.id) else: b = Blog.get(blogId) if b.author != identity.current.user: raise identity.IdentityException() b.date = date b.title = title b.description = description hub.commit() except: hub.rollback() raise hub.end() turbogears.redirect("mypage")
blogオブジェクトのプロパティを変更すると、その内容がDBに反映される。
hub.commitメソッドがそのトリガーだ。
ここでも、blog.authorによるアクセスコントロールをしなければならない。
削除時も同様だろう。
Blogクラスのクラスメソッドとしてしまおう。
def identityGet(cls, id): b = cls.get(id) if b.author != identity.current.user: raise identity.IdentityException() return b identityGet = classmethod(identityGet)
更新などの場合はこちらを使用する。
blog = Blog.identityGet(blogId)
削除もできるようにする。
@expose() @identity.require(identity.not_anonymous()) def deleteBlog(self, blogId=None): hub.begin() try: b = Blog.identityGet(blogId) Blog.delete(b.id) hub.commit() except: hub.rollback() raise hub.end() turbogears.redirect("mypage")
これで、mypageからBlogを作成、更新、削除することができるようになった。
閲覧用ページを作る。
閲覧用にblogメソッドを作成する
from sqlobject import * ... @expose(template="blog.templates.blog") def blog(self, userName): blogs = Blog.select(AND(Blog.q.authorID==User.q.id, User.q.user_name==userName), orderBy=Blog.q.date) return dict(blogs=blogs)
select内の検索条件は、AND関数などを使用して複合条件にすることができる。
また、順序指定のためにorderByキーワード引数にカラムを指定している。
表示するとともに、コメントも受け付けるようにする。
ログイン中はコメントのauthorにユーザー名をデフォルトで出す。
<form action="addComment" method="post"> <input type="hidden" name="blogId" value="${blog.id}"/> <table> <tr> <td> <input py:if="not tg.identity.anonymous" type="input" name="author" value="${tg.identity.user.user_name}"/> <input py:if="tg.identity.anonymous" type="input" name="author"/> </td> </tr> <tr> <td> <textarea name="data"/> </td> </tr> </table> <input type="submit" value="Add"/> </form>
また、コメントも表示する。
<div py:for="comment in blog.comments"> <span py:content="comment.author">comment author <span py:content="comment.data">Comment </div>
コメントの追加は以下のようにする。
@expose() def addComment(self, blogId, data, author): print blogId, data, author hub.begin() try: c = Comment(date=date.today(), blogID=blogId) c.data=data c.author=author hub.commit() except: hub.rollback() raise hub.end() turbogears.redirect("blog?blogId=%s" % blogId)
最初は以下のようにしたのだが、codecエラーが出てしまった。
上記のようにするとなぜか上手くいく。
@expose() def addComment(self, blogId, data, author): hub.begin() try: c = Comment(blogID=blogId, data=data, author=author) hub.commit() except: hub.rollback() raise hub.end() turbogears.redirect("blog?blogId=%s" % blogId)
上記の問題のため、Commentクラスのコンストラクタでdataとauthorを指定しなくてもよいように、Commentクラスの定義を少し修正した。
class Comment(SQLObject): date = DateCol() data = UnicodeCol(default=None) author = UnicodeCol(default=None) blog = ForeignKey("Blog")
テンプレートの修正
ログインやログアウトリンクはmaster.kidにある。
ログイン中は、ログアウトリンクとともに、MyPageへのリンクも表示させよう。
<span py:if="not tg.identity.anonymous"> Welcome ${tg.identity.user.display_name}. <a href="/logout">Logout <a href="/mypage">My Page </span>
トップページの作成
トップページには、最新情報を出すことにしよう。
Blogから日付の降順で、10件分の情報を出す。
@expose(template="blog.templates.welcome") def index(self): blogs = Blog.select(orderBy=Blog.q.date)[:10] return dict(blogs=blogs)
[:10]というのはスライスと呼ばれる操作だ。
この場合は、シーケンスの先頭から10個分の要素を取得する。
sqliteを使用している場合は、この操作はオーバーライドされていて、
limitを使用したクエリの実行として実装されている。
welcome.kidではこのblogリストを表示する。
blogそのものへのリンクとauthorへのリンクを作成する。
<table> <tr py:for="blog in blogs"> <td> ${blog.date} </td> <td> <a href="blog?blogId=${blog.id}" py:content="blog.title"> Blog Title </a> </td> <td> <a py:content="blog.author.display_name" href="blog?userName=${blog.author.user_name}">Blog Author</a> </td> </tr> </table>
次回は入力値チェックなど