Reputation: 1802
In google cloud datastore I have code equivalent to this:
req = datastore.CommitRequest()
req.mode = datastore.CommitRequest.NON_TRANSACTIONAL
foo = req.mutation.insert_auto_id.add()
barListProperty = foo.property.add()
barListValue = []
for i in range(5):
ent = datastore.Entity()
a = ent.property.add()
set_property(a, 'a', 1)
b = ent.property.add()
set_property(b, 'b', i)
set_property(barListProperty, 'barlist', barListValue)
key = datastore.Key()
path_element = key.path_element.add()
path_element.kind = 'Foo'
foo.key.CopyFrom(key)
print datastore.commit(req)
Now I wanted to build the same thing in NDB so I wrote this:
class Foo(Expando):
pass
foo = Foo()
foo.barlist = [Expando(a=1, b=i) for i in range(5)]
foo.put()
But I get the following error:
File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 570, in dispatch
return method(*args, **kwargs)
File "/base/data/home/apps/s~detect-analyze-notify-01a/sjuul.373145649709860280/main.py", line 317, in get
foo.put()
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/model.py", line 3339, in _put
return self._put_async(**ctx_options).get_result()
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 325, in get_result
self.check_success()
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 368, in _help_tasklet_along
value = gen.throw(exc.__class__, exc, tb)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/context.py", line 748, in put
key = yield self._put_batcher.add(entity, options)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 368, in _help_tasklet_along
value = gen.throw(exc.__class__, exc, tb)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/context.py", line 280, in _put_tasklet
keys = yield self._conn.async_put(options, datastore_entities)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/ext/ndb/tasklets.py", line 454, in _on_rpc_completion
result = rpc.get_result()
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 612, in get_result
return self.__get_result_hook(self)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1818, in __put_hook
self.check_rpc_success(rpc)
File "/base/data/home/runtimes/python27/python27_lib/versions/1/google/appengine/datastore/datastore_rpc.py", line 1333, in check_rpc_success
raise _ToDatastoreError(err)
BadRequestError: BLOB, ENITY_PROTO or TEXT properties must be in a raw_property field.
How should I go about this?
EDIT: This didn't work either
class Foo(Expando):
pass
class Bar(Expando):
pass
foo = Foo()
foo.barlist=[Bar(a=1, b=i) for i in range(5)]
foo.put()
Upvotes: 0
Views: 485
Reputation: 1
A good option is to set a new entity for the list. SO you can insert as many items as you may need, as instances of the 'list entity', and you can set the other entity as it parent.
Instead of this (or similar):
foo = Foo()
foo.barlist=[Bar(a=1, b=i) for i in range(5)]
foo.put()
you could try this:
foo = ListFoo(parent= Foo)
foo.item = 'list item'
foo.put()
Upvotes: 0
Reputation: 10579
It looks to me that you're trying to save an entity that itself wasn't persisted yet, which leaves a couple of options:
1) If you want to have the Entity
stored as a separate "row", you can save it, and then store a list of keys:
class Entity(db.Expando):
pass
# Create your main entity.
e = Entity()
e.other_entities = []
# Create a bunch of others.
for i in range(5):
other_entity = Entity(a=i, b=i+1)
other_entity.put()
# Attach the key to the main entity.
e.other_entities.append(other_entity.key())
# Save your main entity.
e.put()
2) If you want the Entity
stored "in-line" you might be able to use the db.EmbeddedEntity
type:
class Entity(db.Expando):
pass
# Create your main entity.
e = Entity()
e.other_entities = []
# Create a bunch of others (but don't save them).
for i in range(5):
other_entity = Entity(a=i, b=i+1)
# Append it to the list as an EmbeddedEntity
e.other_entities.append(db.EmbeddedEntity(other_entity))
# Save your main entity.
e.put()
An example sort of like this is on the main Expando documentation page, where they use db.Text('Text value')
to specify that it should be stored as a TextProperty
and not a StringProperty
.
Upvotes: 0
Reputation: 12986
You can not use the Expando model directly. You will need to create a subclass of ndb.Expando for the repeated property for this to work.
e.g.
s~lightning-catfish> class X(ndb.Expando):
... pass
s~lightning-catfish> class Repeated(ndb.Expando):
... pass
...
s~lightning-catfish> z = X()
s~lightning-catfish> z.y = [Repeated(a=1,b=i) for i in range(5)]
s~lightning-catfish> z.y
[Repeated(a=1, b=0), Repeated(a=1, b=1), Repeated(a=1, b=2), Repeated(a=1, b=3), Repeated(a=1, b=4)]
Upvotes: 1