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

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

また3歩進んで2歩戻る。データストアからデータを引っ張ってくるところが使いにくくなったので書きなおしてみる。





モデルのクラスメソッドやインスタンスメソッドにしていたのですが、このままでも別にいいんですが、違うアプローチに変更してみようと。

「Entry.get_entries()」でオブジェクトを取得するのではなく、ディクショナリが入ったリストを返すようにまでしてしまったのでオブジェクトのまま操作したい場合に不都合が出てきました。
なので「オブジェクトを取得する」、「ハッシュにする」、「リストにする」などの処理を別機能にして、疎結合で使うのが好ましいと考えました。

まず、「model.py」に以下のような裸の関数を書き足します。
/model.py
def get_entry(key):
    return db.get(db.Key(key))


def get_entries():
    return Entry.all().order('-created_at')


def get_category(key):
    return db.get(db.Key(key))


def get_categories():
    return Category.all().order('-created_at')


def delete_items(keys):
    items = db.get(keys)
    if items:
        db.delete(items)
        return True
    else:
        return False
これらの関数を「admin.py」から使えるように下記のようにインポートする行を追記します。
/admin.py
from model import *
少しパフォーマンス的に疑問が残りますが、ダメとわかればその時に書き直します。
記事の一覧を取得するのは
Entry.get_entries()
としていたのを
get_entries()
で取得できるようになります。あまり違いはありませんが、好みと実効性かなとも思う。
ただし前者はハッシュ化されたリストが返ってくるのに対して、後者はオブジェクトそのものが返ってきます。他の「get_entry(key)」も「get_category(key)」も「get_categories()」も同様オブジェクトがそのまま返り値です。このままテンプレートに渡しても機能しますが、これは前に指摘した通り、テンプレート内からモデルにアクセスが走るのでコストが無駄です。

なので、テンプレートに渡すためにこれらオブジェクトをハッシュ化ないしハッシュ化したリスト化する関数を作ってやります。

まずはハッシュ化する関数。
/model.py
class Entry(db.Model):
    title = db.StringProperty()
    body = db.TextProperty()
    categories = db.StringListProperty()
    updated_at = db.DateTimeProperty(auto_now=True)
    created_at = db.DateTimeProperty(auto_now_add=True)

    def to_hash(self):
        return {
            'key': str(self.key()),
            'title': self.title,
            'body': self.body,
            'categories': [get_category(key).to_hash() for key in self.categories],
            'updated_at': utils.to_jst(self.updated_at),
            'created_at': utils.to_jst(self.created_at)
        }
/model.py
class Category(db.Model):
    name = db.TextProperty()
    updated_at = db.DateTimeProperty(auto_now=True)
    created_at = db.DateTimeProperty(auto_now_add=True)

    def to_hash(self):
        return {
            'key': str(self.key()),
            'name': self.name,
            'updated_at': utils.to_jst(self.updated_at),
            'created_at': utils.to_jst(self.created_at)
        }
「Entry」モデル、「Category」モデルそれぞれに「to_hash」というメソッドを追加しました。例えば「entry」という「Entry」モデルから取得したいちオブジェクトに
entry.to_hash()
とすればディクショナリ化いわゆるハッシュの値が返ってきます。

複数オブジェクトの場合はひとつずつ取り出して「to_hash」を食わせる必要があるので、その関数を作ります。
「utils.py」に「to_list」という関数を追加します。
/utils.py
def to_list(obj):
    items = []
    for item in obj:
        items.append(item.to_hash())
    return items
オブジェクト群を受け取って、それぞれ取り出しながらハッシュ化して再リスト化し返すというものです。

唯一この「to_list」という関数が気に入っていない。記事一覧、カテゴリー一覧からも使うので共通化する必要があるのは確かだが、「utils.py」に書くのがよいのか「model.py」に書くのがよいか、いやそもそも関数にする必要がない?
今気づいたがリスト内包表記でいけるのでは?
[entry.to_hash() for entry in get_entries()]
やってみた。いけた…
実行効率もこの方がよいらしい。どこに書くかで迷ってたのがアホらしい…
「utils.py」の「to_list」メソッドは消しておきましょう。

準備が整ったところで「admin.py」のソースを晒してみます。
/admin.py
#!-*- coding:utf-8 -*-
import logging
import os
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from model import Entry, Category
from model import *
from utils import View



##### This view below is "Entry" #################

class EntryList(webapp.RequestHandler, View):
    def get(self):
        self.template_values['entries'] = [item.to_hash() for item in get_entries()]
        self.render()

class EntryNew(webapp.RequestHandler, View):
    def get(self):
        self.template_values['categories'] = [item.to_hash() for item in get_categories()]
        self.render()

    def post(self):
        entry = Entry()
        entry.title = self.request.get("title")
        entry.body = self.request.get("body")
        entry.categories = self.request.get_all("categories")
        entry.put()
        self.redirect("/admin/")

class EntryEdit(webapp.RequestHandler, View):
    def get(self, key):
        def add_select(entry, categories):
            cats = []
            for category in categories:
                select = True if category in entry['categories'] else False
                category.update({'select': select})
                cats.append(category)
            entry['categories'] = cats
            return entry

        entry = get_entry(key).to_hash()
        categories = [item.to_hash() for item in get_categories()]
        self.template_values['entry'] = add_select(entry, categories)
        self.render()

    def post(self, key):
        entry = get_entry(key)
        entry.title = self.request.get("title")
        entry.body = self.request.get("body")
        entry.categories = self.request.get_all("categories")
        entry.put()
        self.redirect("/admin/")

class EntryDelete(webapp.RequestHandler):
    def get(self, key):
        delete_items(key)
        self.redirect("/admin/")



##### This view below is "Category" #################

class CategoryList(webapp.RequestHandler, View):
    def get(self):
        self.template_values['categories'] = [item.to_hash() for item in get_categories()]
        self.render()

class CategoryNew(webapp.RequestHandler, View):
    def get(self):
        self.render()

    def post(self):
        category = Category()
        category.name = self.request.get("name")
        category.put()
        self.redirect("/admin/category/")

class CategoryEdit(webapp.RequestHandler, View):
    def get(self, key):
        self.template_values['category'] = get_category(key).to_hash()
        self.render()

    def post(self, key):
        category = db.get(db.Key(key))
        category.name = self.request.get("name")
        category.put()
        self.redirect("/admin/category/")

class CategoryDelete(webapp.RequestHandler):
    def get(self, key):
        delete_items(key)
        self.redirect("/admin/category/")
なんか少し洗練されてきた感じがしますね。満足してはいけませんが、美しいコードになるとやっぱりしっくりきます。


しかし中々前に進みませんなぁ


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












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