GAEのデータストアを使ってみました

今回はDatastoreを使ってみました。
チュートリアルではGQLというデータベースを操作するための言語を使用した例を中心に説明されているのですが、こちらの記事などによるとGQLはこれまでにSQLを経験したことがある人がGAEを容易に使用できるようにするためのもので(GQLを使わず)Pythonで直接APIを操作することもできるようです。
私はそもそもSQLを全く知らないのであえてGQLを使う必要もないと考えPythonAPIを操作する方法で書いてみました。

名前、性別、身長、体重をデータストアに保存し、名前で検索できるものを作ってみることにします。
コードがかなり長くなったので全体はエントリの最後に載せています。
ここではデータストアに関連する部分のみ抜粋します。
エンティティのモデルを作るクラスの定義はGQLを使う場合と同じです。

class DataStoreTest(db.Model):
    name = db.StringProperty()
    sex = db.IntegerProperty()
    height = db.FloatProperty()
    weight = db.FloatProperty()
    date = db.DateTimeProperty(auto_now_add=True)

日付型の情報はDateTimeProperty()ですが引数で"auto_now_add=True"を指定しておくと保存時に自動的にその時点の時刻が記録されます。
実際のデータの保存は

person = DataStoreTest(name=req_name)
person.sex = int(self.request.get('Sex'))
person.height = float(self.request.get('Height'))
person.weight = float(self.request.get('Weight'))
            
person.put()

というように上で定義したクラスのインスタンスを作成しプロパティに各情報を代入してエンティティを作成。
put()メソッドを実行するとデータストアに書き込まれます。

データを取り出すには

persons = db.Query(DataStoreTest).filter('name =', req_name).order('-date')

というようにフィルターを使っています。これを分解すると

db.Query(DataStoreTest) # DataStoreTestクラスから作成されたエンティティを取得
.filter('name =', req_name) #フィルターでnameプロパティがreq_name変数の中身と一致するものを抽出
.order('-date') # 抽出されたデータを日付順に並び替え

という流れです。

データストアが使えると一気にできることが広がるので楽しいですね!

以下今回書いたコード全体です。

# coding: utf-8

import webapp2
import cgi
from google.appengine.ext import db


class DataStoreTest(db.Model):
    name = db.StringProperty()
    sex = db.IntegerProperty()
    height = db.FloatProperty()
    weight = db.FloatProperty()
    date = db.DateTimeProperty(auto_now_add=True)


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/html;charset=utf-8'
        self.response.out.write('''
                                <!DOCTYPE html>
                                <html>
                                  <head>
                                    <title>
                                      データストアテスト
                                    </title>
                                  </head>
                                  <body>
                                    <form action="/" method="post">
                                      <input type="radio" name="isReg" value="1" id="wREG" checked="checked"><label for="wREG">登録</label>
                                      <input type="radio" name="isReg" value="0" id="wSRC"><label for="wSRC">検索</label>
                                      <br>
                                      名前
                                      <input type="text" name="Name">
                                      <br>
                                      性別
                                      <input type="radio" name="Sex" value="1" id="sMALE" checked="checked"><label for="sMALE">男性</label>
                                      <input type="radio" name="Sex" value="2" id="sFEMALE"><label for="sFEMALE">女性</label>
                                      <input type="radio" name="Sex" value="3" id="sUNKNOWN"><label for="sUNKNOWN">登録しない</label>
                                      <br>
                                      身長
                                      <input type="number" step="0.1" name="Height">
                                      <br>
                                      体重
                                      <input type="number" step="0.1" name="Weight">
                                      <br>
                                      <input type="submit">
                                    </form>
                                  </body>
                                </html>
                                ''')
    
    @staticmethod
    def _dataformer(person):
        sexL =[u"男", u"女", u"未登録"]
        
        text = "<tr><td>"
        text += cgi.escape(person.name)
        text += "</td><td>"
        text += sexL[person.sex - 1]
        text += "</td><td>"
        text += cgi.escape(str(person.height))
        text += "</td><td>"
        text += cgi.escape(str(person.weight))
        text += "</td></tr>"
        
        return text
    
    def post(self):
        req_work = int(self.request.get('isReg').strip())
        req_name = self.request.get('Name').strip()
        
        if not req_name:  # Name未記入の場合はgetの場合を実行
            self.get()
            return
        
        if req_work:
            person = DataStoreTest(name=req_name)
            person.sex = int(self.request.get('Sex'))
            person.height = float(self.request.get('Height'))
            person.weight = float(self.request.get('Weight'))
            
            person.put()
            
            self.response.headers['Content-Type'] = 'text/html;charset=utf-8'
            self.response.out.write('''
                                    <!DOCTYPE html>
                                    <html>
                                      <head>
                                        <title>
                                          登録完了
                                        </title>
                                      </head>
                                      <body>
                                        登録しました。
                                        <br>
                                        <a href="/">戻る</a>
                                      </body>
                                    </html>
                                    ''')
            return
        
        if not req_work:
            persons = db.Query(DataStoreTest).filter('name =', req_name).order('-date')
            
            self.response.headers['Content-Type'] = 'text/html;charset=utf-8'
            self.response.out.write('''
                                    <!DOCTYPE html>
                                      <html>
                                        <head>
                                          <title>
                                            問い合わせ結果
                                          </title>
                                          <style type="text/css">
                                            <!--
                                              table,tr,td {border: solid 1px black; border-collapse: collapse;}
                                            -->
                                          </style>
                                        </head>
                                      <body>
                                        <table>
                                          <thead>
                                            <tr><td>名前</td><td>性別</td><td>身長</td><td>体重</td></tr>
                                          </thead>
                                        <tbody>
                                    ''')
            
            for person in persons:
                self.response.out.write(self._dataformer(person))

            self.response.out.write('''
                                          </tbody>
                                        </table>
                                        <br>
                                        <a href="/">戻る</a>             
                                      </body>
                                    </html>
                                    ''')
            
        

app = webapp2.WSGIApplication([('/', MainPage)],
                            debug=True)