DB to NDB Client Library Migration Stay organized with collections Save and categorize content based on your preferences.
No Datastore changes needed
In case you wondered, despite the different APIs, NDB and the old ext.dbpackage writeexactly the same data to the Datastore. That means youdon’t have to do any conversion to your datastore, and you can happily mix andmatch NDB and ext.db code, as long as the schema you use is equivalent. You caneven convert between ext.db and NDB keys usingndb.Key.from_old_key()andkey.to_old_key().
General differences
NDB is picky about types. E.g. in db, when a key is required, you can also passan entity or a string. In NDB you must pass a key.
NDB is picky about lists. E.g. in db,
db.put()takes either an entity or a listof entities. In NDB, you useentity.put()to put a single entity, butndb.put_multi(<list>)to put a list of entities.NDB prefers methods over functions. E.g. instead of
db.get(key), anddb.put(entity), NDB useskey.get()andentity.put().NDB doesn't like offering two APIs that do the same thing. (On the other handit does sometimes offer two APIs that do slightly different things.)
Side-by-side API call comparison
The tables below show similarities and differences between ndb and the oldext.db module. See theOfficial NDB Docsfor an introduction to and reference for NDB.
Model class
| google.appengine.ext.db | ndb.model |
classMyModel(db.Model):foo=db.StringProperty() | classMyModel(ndb.Model):foo=ndb.StringProperty() |
@classmethoddefkind(cls):return'Foo' | @classmethoddef_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 () !! |
Entities
| 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() | No direct equivalent; seeStack Overflowfor a possible solution. |
Get
| 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.keyforxinlist_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) |
Delete
| 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.keyforminlist_of_model_instances]) |
db.delete(list_of_keys) | ndb.delete_multi(list_of_keys) |
Properties
| 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() | No equivalent. |
db.IntegerProperty() | ndb.IntegerProperty() |
db.LinkProperty() | ndb.StringProperty() Has a max size of 500. For longerURLs, use |
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.propMyModel.prop \.get_value_for_datastore \(model_instance) | ndb.KeyProperty(kind=AnotherModel)model_instance.prop.get()model_instance.prop |
# Using the backreference setother=model_instance.propother.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) | Not supported; strings are always allowed to contain |
db.StringListProperty() | ndb.StringProperty(repeated=True) |
db.TextProperty() | ndb.TextProperty() |
db.TimeProperty() | ndb.TimeProperty() |
db.UserProperty() | ndb.UserProperty() |
blobstore.BlobReferenceProperty() | ndb.BlobKeyProperty() |
Building a Key
| 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') |
Key operations
| 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()isNone# or...model_instance.has_complete_key() |
key.app(),key.namespace(),key.parent(),key.kind() | Same. |
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)assertS <=X |
Transactions
| 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) |
Queries
| google.appengine.ext.db | ndb.model |
q=MyModel.all() | q=MyModel.query() |
forresultinq.run():... | forresultinq.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 |
classMyExpando(Expando):passq=MyExpando.all()q.filter('foo =','bar') | classMyExpando(Expando):passq=MyExpando.query(ndb.GenericProperty('foo')=='bar') |
classFoo(Model):...classBar(Model):foo=ReferenceProperty(Foo)myfoo= <someFooinstance>forbarinmyfoo.bar_set():... | classFoo(Model):...classBar(Model):foo=KeyProperty(kind=Foo)myfoo= <someFooinstance>forbarin \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 |
Cursors
q=MyModel.all()a=q.fetch(20)cur=q.cursor() | q=MyModel.query()a,cur,more=q.fetch_page(20) In NDB, |
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) |
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-06-16 UTC.