ndb (Next DB モジュール)

NDB (Next DB モジュール) は Google App Engine の Python ランタイムで利用可能な新しいデータストア API です。

以前は db (google.appengine.ext.db) という API がありましたが・・・、というか、現在でも利用可能ですが、 基本的に下位互換のために存在するもので、今後、新しいプログラムには ndb を利用していくと良いと思います。

Python の生みの親であるグイド・バンロッサム氏が率いるプロジェクトで開発されています。 実験的 (experimental) というステータスでリリースされていたのですが、2012年3月に正式版 (GA, General Availability) としてリリース。 Google App Engine 1.6.4 に標準コンポーネントとして組み込まれました。

App Engine Datastore API (db) との違い (概要)

ndb はより洗練された、優れた API として、それまでの App Engine Datastore API の代わりとして使える API として開発されました。

不要と思われるものは削除しつつ、重要な機能を追加しています。追加機能としては主に三つ。

  • StructuredProperty クラスの導入。これによってデータエンティティが入れ子の構造を持てるようになりました。
  • キャッシュの自動化。イン・コンテキスト・キャッシュや Memcache を利用し高速の読み込みが可能となります。
  • 非同期 API による平行処理を可能としています。

db から ndb へ

では、db (Google App Engine Datastore API) から ndb へと移行するとき、 具体的にコードを書くに当たってどのように違いがあるのでしょうか。

Google が公開している "NDB Cheat Sheet" には次のような記載があります。(私が日本語訳にしました。 厳密には原文を参照してください)

  • NDB は型にうるさいです。例えば、db でキーが必要な時、entity でも string でも渡せたのですが、 ndb ではキーを渡す必要があります。
  • NDB はリストにうるさいです。例えば、db の db.put() ではエンティティーでもそのリストでも受けとるのですが、 ndb では単一のエンティティを保存するなら エンティティーの put メソッドを、エンティティーのリストを保存するなら、 ndb.put_multi(リスト) を使います。
  • NDB では関数よりメソッドを好みます。例えば、db.get(キー) とか db.put(エンティティー) とやる代わりに、 NDB は key.get() とか entity.put() とします。
  • NDB では同じ事をするのに二つの API を提供するのは好みません。 (と、その一方で、ちょっと違う二つの事をするのに、二つの API をそれぞれ提供したりします)

ndb を使ってみる

ndb の利用例として、webapp を利用して、名前を登録、それを表示する、といった簡単なことをするコードを書いてみました。

import os

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import ndb

class Person(ndb.Model):
    first_name = ndb.StringProperty()
    last_name = ndb.StringProperty()
    age = ndb.IntegerProperty()
    
class MainPage(webapp.RequestHandler):
     
    def get(self):    
        people = Person.query()
        template_values = {
            'people' : people
        }
        template_path = os.path.join(
            os.path.dirname(__file__), 
            'helloworld.html')
        self.response.out.write(template.render(
            template_path, template_values))
        
    def post(self):
        
        p = Person(
            first_name = self.request.get("fname"),
            last_name = self.request.get("lname"),
            age = int(self.request.get("age")))
        p.put()
        
        self.redirect("/")
        
        
application = webapp.WSGIApplication([('/', MainPage)],
	 debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

ちなみにテンプレートはこんな感じで。シンプルにしたかったので escape とかフィルター系は何もしてませんのでご注意。

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<table>
{% for p in people %}
<tr>
	<td>{{p.first_name}} {{p.last_name}}</td>
	<td>{{p.age}}</td>
</tr>
{% endfor %}
</table>

<h2>Add Entry</h2>
<form method="post" action="/">
<table>
<tr><th>First Name:</th>
<td><input type="text" name="fname"></td></tr>
<tr><th>Last Name:</th>
<td><input type="text" name="lname"></td></tr>
<tr><th>Age:</th>
<td><input type="text" name="age"></td></tr>
<tr><td colspan="2">
<input type="submit" value="Add"></td></tr>
</table>
</form>

</body>
</html>

具体的な説明はまた別の記事で書きたいと思います。 ここでは、「パッとコードを見ても、どこが ext.db と違うか分からないくらい同様な感じでコードがかけるんだな」ということを感じてもらえれば十分と思います。