DB から NDB クライアント ライブラリへの移行

Datastore の変更は不要

異なる API にも関わらず、NDB と古い ext.db パッケージがまったく同じデータをデータストアに書き込むのは不思議に感じるかもしれません。スキーマが同じであれば、データベースの変換を行う必要はなく、NDB と ext.db コードを混在させることができます。また、ndb.Key.from_old_key()key.to_old_key() を使用して、ext.db キーと NDB キーの間で変換を行うこともできます。

全般的な違い

  • NDB はタイプの扱いが厳密です。db では、キーが必要な場合、エンティティまたは文字列を渡すことができます。NDB ではキーを渡す必要があります。

  • NDB はリストの扱いが厳密です。たとえば、db では、db.put() はエンティティまたはエンティティのリストを受け取ります。NDB では、entity.put() を使用して単一のエンティティを配置しますが、エンティティのリストは ndb.put_multi(<list>) を使用して配置します。

  • NDB は関数よりもメソッドを使用します。たとえば、NDB では、db.get(key)db.put(entity) ではなく key.get()entity.put() を使用します。

  • NDB では、同じ処理の実行に 2 つの API を提供しません。処理が少しでも異なれば、2 つの API を提供する場合もあります。

API 呼び出しの比較

以下の表では、ndb と古い ext.db モジュールの類似点と相違点を示します。NDB の説明とリファレンスについては、NDB の公式ドキュメントをご覧ください。

モデルクラス

google.appengine.ext.db ndb.model
class MyModel(db.Model):
  foo = db.StringProperty()
class MyModel(ndb.Model):
  foo = ndb.StringProperty()
@classmethod
def kind(cls):
  return 'Foo'
@classmethod
def _get_kind(cls):
  return 'Foo'
MyModel.kind()
MyModel._get_kind()
MyModel.properties()
model_instance.properties()
MyModel._properties  # No () !!
model_entity._properties
MyExpando.dynamic_properties()
MyExpando._properties  # No () !!

エンティティ

google.appengine.ext.db ndb.model
MyModel(key_name='my_key')
MyModel(id='my_key')
MyModel(key_name='my_key',
  parent=model_instance)
MyModel(id='my_key',
  parent=model_instance.key)
key = model_instance.key()
key = model_instance.key  # No () !!
model_instance = MyModel(
  foo='foo',
  bar='bar',
  baz='baz')
model_instance = MyModel(
  foo='foo',
  bar='bar',
  baz='baz')
model_instance.foo = 'foo'
model_instance.bar = 'bar'
model_instance.baz = 'baz'
model_instance.foo = 'foo'
model_instance.bar = 'bar'
model_instance.baz = 'baz'
# or a shortcut...
model_instance.populate(
  foo='foo',
  bar='bar',
  baz='baz')
model_instance.is_saved()

同等のものはありません。可能な解決策については、Stack Overflow をご覧ください。

取得

google.appengine.ext.db ndb.model
MyModel.get_by_key_name('my_key')
MyModel.get_by_id('my_key')
MyModel.get_by_id(42)
MyModel.get_by_id(42)
db.get(key)
key.get()
MyModel.get(key)
key.get()
db.get(model_instance)
model_instance.key.get()
db.get(list_of_keys)
ndb.get_multi(list_of_keys)
db.get(list_of_instances)
ndb.get_multi([x.key for x in
               list_of_instances])
MyModel.get_or_insert('my_key',
  parent=model_instance,
  foo='bar')
MyModel.get_or_insert('my_key',
  parent=model_instance.key,
  foo='bar')

put

google.appengine.ext.db ndb.model
db.put(model_instance)
model_instance.put()
db.put(list_of_model_instances)
ndb.put_multi(
  list_of_model_instances)

削除

google.appengine.ext.db ndb.model
model_instance.delete()
model_instance.key.delete()
db.delete(model_instance)
model_instance.key.delete()
db.delete(key)
key.delete()
db.delete(list_of_model_instances)
ndb.delete_multi([m.key for m in
  list_of_model_instances])
db.delete(list_of_keys)
ndb.delete_multi(list_of_keys)

プロパティ

google.appengine.ext.db ndb.model
db.BlobProperty()
ndb.BlobProperty()
db.BooleanProperty()
ndb.BooleanProperty()
db.ByteStringProperty()
ndb.BlobProperty(indexed=True)
db.CategoryProperty()
ndb.StringProperty()
db.DateProperty()
ndb.DateProperty()
db.DateTimeProperty()
ndb.DateTimeProperty()
db.EmailProperty()
ndb.StringProperty()
db.FloatProperty()
ndb.FloatProperty()
db.GeoPtProperty()
ndb.GeoPtProperty()
db.IMProperty()

同等のものはありません。

db.IntegerProperty()
ndb.IntegerProperty()
db.LinkProperty()
ndb.StringProperty()

最大サイズは 500 です。これより長い URL の場合には ndb.TextProperty() を使用します。

db.ListProperty(bool)
db.ListProperty(float)
db.ListProperty(int)
db.ListProperty(db.Key)
# etc.
ndb.BooleanProperty(repeated=True)
ndb.FloatProperty(repeated=True)
ndb.IntegerProperty(repeated=True)
ndb.KeyProperty(repeated=True)
# etc.
db.PhoneNumberProperty()
ndb.StringProperty()
db.PostalAddressProperty()
ndb.StringProperty()
db.RatingProperty()
ndb.IntegerProperty()
db.ReferenceProperty(AnotherModel)
model_instance.prop
MyModel.prop \
  .get_value_for_datastore \
  (model_instance)
ndb.KeyProperty(kind=AnotherModel)
model_instance.prop.get()
model_instance.prop
# Using the backreference set
other = model_instance.prop
other.prop_set.fetch(N)
# No direct equivalent; emulation:
other = model_instance.prop.get()
MyModel.query(
  MyModel.prop == other.key).fetch(N)
db.SelfReferenceProperty()
ndb.KeyProperty(kind='ThisModelClass')
db.StringProperty()
ndb.StringProperty()
db.StringProperty(multiline=True)

サポートされていません。文字列には \n を入れることができます。

db.StringListProperty()
ndb.StringProperty(repeated=True)
db.TextProperty()
ndb.TextProperty()
db.TimeProperty()
ndb.TimeProperty()
db.UserProperty()
ndb.UserProperty()
blobstore.BlobReferenceProperty()
ndb.BlobKeyProperty()

キーの作成

google.appengine.ext.db ndb.model
key = db.Key(encoded_key)
key = ndb.Key(urlsafe=encoded_key)
key = db.Key.from_path(
  'MyKind', 'some_id',
  'MyKind', 'some_id')
key = ndb.Key(
  'MyKind', 'some_id',
  'MyKind', 'some_id')
key = db.Key.from_path(
  MyModel, 'some_id',
  parent=model_instance,
  namespace='my_namespace')
key = ndb.Key(
  MyModel, 'some_id',
  parent=model_instance.key,
  namespace='my_namespace')

キー操作

google.appengine.ext.db ndb.model
key.id_or_name()
key.id()
key.id()
key.integer_id()
key.name()
key.string_id()
key.has_id_or_name()
key.id() is None
# or...
model_instance.has_complete_key()
key.app(), key.namespace(),
key.parent(), key.kind()

同じです。

str(key)
key.urlsafe()
key.to_path()
key.flat()
db.allocate_ids(MyModel, size)
S, E = MyModel.allocate_ids(size)
db.allocate_id_range(MyModel,X,Y)
S, E = MyModel.allocate_ids(max=Y)
assert S <= X

トランザクション

google.appengine.ext.db ndb.model
db.run_in_transaction(function)
ndb.transaction(function)
db.run_in_transaction(
  function, *args, **kwds)
ndb.transaction(
  lambda: function(*args, **kwds))
db.run_in_transaction_custom_retries(n, function)
ndb.transaction(function, retries=n)
opts = \
  db.create_transaction_options(xg=True)
db.run_in_transaction_options(opts, fun)
ndb.transaction(fun, xg=True)

クエリ

google.appengine.ext.db ndb.model
q = MyModel.all()
q = MyModel.query()
for result in q.run(): ...
for result in q.iter(): ...
q = MyModel.all() \
  .filter('foo =', 'bar') \
  .filter('baz >=', 'ding')
q = MyModel.query(
  MyModel.foo == 'bar',
  MyModel.baz >= 'ding')
q = MyModel.all()
q.filter('foo =', 'bar')
q.filter('baz >=', 'ding')
q.order('-foo')
results = q.fetch(10)
q = MyModel.query()
q = q.filter(MyModel.foo == 'bar')
q = q.filter(MyModel.baz >= 'ding')
q = q.order(-MyModel.foo)
results = q.fetch(10)
q.filter('__key__', k)
# k is a db.Key instance
q = q.filter(MyModel._key == k)
# k is an ndb.Key instance
a.filter('__key__ >=', k)
# k is a db.Key instance
q = q.filter(MyModel._key >= k)
# k is an ndb.Key instance
class MyExpando(Expando): pass
q = MyExpando.all()
q.filter('foo =', 'bar')
class MyExpando(Expando): pass
q = MyExpando.query(
 ndb.GenericProperty('foo') == 'bar')
class Foo(Model): ...
class Bar(Model):
  foo = ReferenceProperty(Foo)
myfoo = <some Foo instance>
for bar in myfoo.bar_set(): ...
class Foo(Model): ...
class Bar(Model):
  foo = KeyProperty(kind=Foo)
myfoo = <some Foo instance>
for bar in \
  Bar.query(Bar.foo == myfoo.key): ...
q = MyModel.all()
q.ancestor(ancestor_key)
q =
  MyModel.query(ancestor=ancestor_key)
q = MyModel.all(keys_only=True)
r = q.fetch(N)
r = MyModel.query() \
    .fetch(N, keys_only=True)
# Alternatively:
q = MyModel.query(
      default_options=QueryOptions(
                      keys_only=True))
r = q.fetch(N)
q = MyModel.gql(...)
# same thing

カーソル

q = MyModel.all()
a = q.fetch(20)
cur = q.cursor()
q = MyModel.query()
a, cur, more = q.fetch_page(20)

NDB では、more は、カーソルに複数のエンティティがあるかどうかを表すブール値です。

q.with_cursor(cur)
b = q.fetch(20)
b, cur, more = \
  q.fetch_page(20, start_cursor=cur)
q.with_cursor(end_cursor=cur)
b = q.fetch(20)
q.fetch(20, end_cursor=cur)