アットウィキロゴ

Google App Engineメモ

どこまでがappengineの話でどこからがpythonなのかDjangoなのか、とか区別せずに書いているので注意。

!!dev_appserver.py

  1. --require_indexes index.yamlに無いインデックスを使用するクエリが実行された時に、index.yamlにインデックスを追加しない。つまり、アップロード後と同じ状態で実行できる。
  2. --clear_datastore datastoreを全て消去する。

datastore関連

ユニークなキーについて。

"entity.key().id()"で取得できる。

class MyModel(db.Model):
 text = db.StringProperty()

myModel = MyModel(text="hoge")
id = myModel.key().id()

myModel2 = MyModel.get_by_id(id)

別Modelへの参照について。

"db.ReferenceProperty(Modelクラス名)"を使用する。

  1. 作成方法
    class MyModel1(db.Model):
      text = db.StringProperty()
    class MyModel2(db.Model)
      text = db.StringProperty()
      myModel1 = db.ReferenceProperty(MyModel1)
    
  2. 取得方法 普通の値のバインドと同じように使用できる。
    myModel1 = MyModel1.get_by_id(1)
    myModel2s = MyModel2.gql("WHERE myModel1=:bind", bind=myModel1")
    
    注意点として「永続化されていないEntity(Modelのインスタンス)を設定する事はできない」という注意点がある。
  3. template内での使用方法
    {{ myModel1.key.id }}
    
    みたいなカンジで。

親子関係について

Googleのデータは親子関係を指定できる。Modelのparent属性で設定できる。

  1. 設定方法
    class MyModelRoot(db.Model):
      text = db.StringProperty()
      date = db.DateTimeProperty(auto_now_add=True)
    
    class MyModel(db.Model):
      text = db.StringProperty()
      date = db.DateTimeProperty(auto_now_add=True)
    
    # 親子関係を作らない。
    model = MyModel(text="hoge")
    
    # 親子関係を作る。
    root = MyModelRoot(text="fuga")
    model = MyModel(parent=root, text="hoge") # parent=root,
    
  1. parentを指定した取得方法 "ANCESTOR IS"を使用する。
    parentKey = 1
    root = MyModelRoot.get_by_id(key)
    models = MyModel.gql("WHERE ANCESTOR IS :parent", parent=root)
    

Transactionについて。

"db.run_in_transaction(関数, 関数へのパラメータ...)"を使用する。

 class MyModel1(db.Model):
   text = db.StringProperty()
   anotherModelInfo = db.NumberProperty()
 class MyModel2(db.Model):
   ref = db.ReferenceProperty(MyModel1)
 
 class Controller(webapp.RequestHandler):
   def post(self):
     datas = []
     ... # MyModelのlistであるdatasを作成。
     db.run_in_transaction(self.insert, datas)
 
   def insert(datas):
     for data in datas:
       data.put()

以下の制限に注意。

  1. Transaction中にクエリを発行できない("BadRequestError: Can't query inside a transaction."なる例外が発生する)。 必要なデータはTransaction開始前にあらかじめ全部作っておいて、put()やdelete()の更新操作のみをTransactionでくるんでやるような、小さなTransactionを心がける必要がある。 ReferencePropertyの「永続化されていないEntity(Modelのインスタンス)を設定する事はできない」という制限も考えると、工夫が必要な場合もあるが、Transactionを小さくするという意味では安全な設計を心がける良い練習になるかもなぁ。
  1. Transaction中でクエリが使えないと、更新対象に対して関連のあるEntityを芋づるで削除したいときに困るかも。そんな時はModel.get_by_id()を使って更新対象を取得すれば良い。 例えば以下のような例。
    class MyModel1(db.Model):
     title = StringProperty()
    class MyModel2(db.Model):
     ref = ReferenceProperty(MyModel1)
     otherRef = ReferenceProperty(OtherModel)
    
    def method():
      model1 = MyModel1.get_by_id(int(idString))
      model2s = MyModel2.gql("WHERE ref = :ref", ref=model1)
      db.run_in_transaction(delete_cascade, model1, model2s)
    
    def delete_cascade(model1, model2s)
     db.delete(model2s)
     model1.delete()
    
    一見すると、run_in_transaction()の中ではクエリは無いように見えるが"db.delete(model2s)"実行時に内部的に"WHERE ref=:ref"のクエリが走るため、そこで"BadRequestError: Can't query inside a transaction."となってしまう。ただし、クエリが使えないといってもGqlQuery系が使えない、という意味なので"Model.get_by_id()"を使う事は問題ないみたいだ。
    def method():
      model1 = MyModel1.get_by_id(int(idString))
      _model2s = MyModel2.gql("WHERE ref = :ref", ref=model1)
      model2Ids = []
      for m2 in _model2s:
        model2Ids.append(m2.key().id())
      model2s = MyModel2.get_by_id(ids=model2Ids)
      db.run_in_transaction(delete_cascade, model1, model2s)
    
    idのリストを作って…というのが面倒だが、今のところこれしか方法がわからない。

logging(ロギング)

ログの出力には「import logging」でimportしたモジュールを使えば良いらしい。Javaに似ていて使いやすい。main()メソッド内でLoggerのLogLevelを設定する。

import logging

def myMethod(hoge):
  logging.getLogger().debug("myMethod start.")
  ...
  logging.getLogger().debug("myMethod end.")

class MyHandler(webapp.RequestHandler):
  def post(self):
    logging.getLogger().info("MyHandler.post start.")
    ...

def main():
 logging.getLogger().setLevel(logging.DEBUG)
 application = webapp.WSGIApplication(
   [('/', MainPage)], debug=True)
 wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
  main()
最終更新:2008年05月17日 16:52