Skip to content
This repository was archived by the owner on May 6, 2026. It is now read-only.
This repository was archived by the owner on May 6, 2026. It is now read-only.

Problem reading/writing models saved by legacy Appengine #623

@mecheverri

Description

@mecheverri

Steps to reproduce:

  1. Using the appengine NDB package with python2.7, I ran the following code:
from google.appengine.ext import ndb

class TestInnerModel(ndb.Model):
    s = ndb.StringProperty(required=True, indexed=False)
    snake =  ndb.StringProperty(required=True, default="cobra", indexed=False)
    b0 = ndb.BooleanProperty(required=True, default=True, indexed=False)
    b1 = ndb.BooleanProperty(required=True, default=False, indexed=False)

class TestOuterModel(ndb.Model):
    imods = ndb.LocalStructuredProperty(TestInnerModel, repeated=True, indexed=False, compressed=True)

OMID = "OMID000000"
im0 = TestInnerModel(s="hi")
im0.put()
im1 = TestInnerModel()
im1.s = "ho"
im1.put()
im2 = TestInnerModel(s="there", snake="rattler")
im2.put()
om0 = TestOuterModel(id=OMID)
om0.imods = [TestInnerModel(s="hi"), TestInnerModel(s="ho"), TestInnerModel(s="there", snake="rattler")]
om0.put()
  1. Then, with python 3.9 and google-cloud-ndb 1.7.3, I ran the following (with a couple of Redis parameters omitted):
from google.cloud import ndb
import redis

def client_context_init():
    return Client().context(
        global_cache=RedisCache(redis.Redis(connection_pool=redis.ConnectionPool())),
        legacy_data=True
    )

class TestInnerModel(ndb.Model):
    s = ndb.StringProperty(required=True)
    snake =  ndb.StringProperty(required=True, default="cobra")
    b0 = ndb.BooleanProperty(required=True, default=True, indexed=False)
    b1 = ndb.BooleanProperty(required=True, default=False, indexed=False)

class TestOuterModel(ndb.Model):
    imods = ndb.LocalStructuredProperty(TestInnerModel, repeated=True, indexed=False, compressed=True)

OMID = "OMID000000"
with client_context_init():
    for im in TestInnerModel.query():
        print(im.to_dict())
    om0 = TestOuterModel.get_by_id(OMID)
    om0.imods.append(TestInnerModel(s="yo"))
    om0.put()

This failed on om0.put() with the following error:

Traceback (most recent call last):
  File "<stdin>", line 8, in <module>
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/_options.py", line 89, in wrapper
    return wrapped(*pass_args, **kwargs)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/utils.py", line 114, in wrapper
    return wrapped(*args, **new_kwargs)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/utils.py", line 146, in positional_wrapper
    return wrapped(*args, **kwds)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 5134, in _put
    return self._put_async(_options=kwargs["_options"]).result()
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/tasklets.py", line 191, in result
    self.check_success()
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/tasklets.py", line 138, in check_success
    raise self._exception
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/tasklets.py", line 315, in _advance_tasklet
    yielded = self.generator.send(send_value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 5194, in put
    ds_entity = _entity_to_ds_entity(self)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 748, in _entity_to_ds_entity
    prop._to_datastore(entity, data)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 4334, in _to_datastore
    keys = super(LocalStructuredProperty, self)._to_datastore(
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 2496, in _to_datastore
    keys = super(BlobProperty, self)._to_datastore(
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 2082, in _to_datastore
    value = self._get_base_value_unwrapped_as_list(entity)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1558, in _get_base_value_unwrapped_as_list
    wrapped = self._get_base_value(entity)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1544, in _get_base_value
    return self._apply_to_values(entity, self._opt_call_to_base_type)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1857, in _apply_to_values
    value[:] = map(function, value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1619, in _opt_call_to_base_type
    value = _BaseValue(self._call_to_base_type(value))
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1678, in _call_to_base_type
    value = call(value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1824, in call
    new_value = method(self, value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 4277, in _to_base_type
    pb = _entity_to_protobuf(value, set_key=self._keep_keys)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 789, in _entity_to_protobuf
    ds_entity = _entity_to_ds_entity(entity, set_key=set_key)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 748, in _entity_to_ds_entity
    prop._to_datastore(entity, data)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 2082, in _to_datastore
    value = self._get_base_value_unwrapped_as_list(entity)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1558, in _get_base_value_unwrapped_as_list
    wrapped = self._get_base_value(entity)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1544, in _get_base_value
    return self._apply_to_values(entity, self._opt_call_to_base_type)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1860, in _apply_to_values
    new_value = function(value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1619, in _opt_call_to_base_type
    value = _BaseValue(self._call_to_base_type(value))
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1678, in _call_to_base_type
    value = call(value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 1824, in call
    new_value = method(self, value)
  File "/vagrant/typhoon/venv/lib/python3.9/site-packages/google/cloud/ndb/model.py", line 2251, in _validate
    raise exceptions.BadValueError("Expected bool, got {!r}".format(value))
google.cloud.ndb.exceptions.BadValueError: Expected bool, got 1

So there's nothing wrong with the TestInnerModel instances themselves, but saving the modified TestOuterModel fails. The code seems to run OK if the LocalStructuredProperty has compressed=False.

Metadata

Metadata

Assignees

Labels

api: datastoreIssues related to the googleapis/python-ndb API.priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions