Skip to content

Serializing / Deserializing Entity without ID.  #384

@smittysmee

Description

@smittysmee

Issue:
There is a new issue with serializing and deserializing an entity that has a namespace defined for the model. This is a new issue that appeared in 1.1.2 release following this commit: 44f02e4.
PR: #339

I believe this code can be fixed by looking at the ndb.Model's __init__:

key_parts_unspecified = (
            id_ is None
            and parent is None
            and project is None
            and namespace is key_module.UNDEFINED
        )  # Needs to check if `_id` is None apart from `namespace` or something similar

Environment details:
Python 3.7.3
google-cloud-ndb 1.1.2

Stack:

Error
Traceback (most recent call last):
  File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/unittest/case.py", line 615, in run
    testMethod()
  File "/Users/asmith/development/csg-python-libs/ndb/unit_tests/test_properties.py", line 89, in test_namespaced_unsaved_entity_pickling
    entity = pickle.loads(pickle.dumps(entity))
  File "/Users/asmith/development/virtual_environments/csg-python-libs/lib/python3.7/site-packages/google/cloud/ndb/key.py", line 473, in __setstate__
    *flat, project=project, namespace=kwargs["namespace"]
  File "/Users/asmith/development/virtual_environments/csg-python-libs/lib/python3.7/site-packages/google/cloud/datastore/key.py", line 114, in __init__
    self._path = self._combine_args()
  File "/Users/asmith/development/virtual_environments/csg-python-libs/lib/python3.7/site-packages/google/cloud/datastore/key.py", line 214, in _combine_args
    child_path = self._parse_path(self._flat_path)
  File "/Users/asmith/development/virtual_environments/csg-python-libs/lib/python3.7/site-packages/google/cloud/datastore/key.py", line 198, in _parse_path
    raise ValueError(id_or_name, "ID/name was not a string or integer.")
ValueError: (None, 'ID/name was not a string or integer.')

Test Code:

# import ...  # Update for your code context
# ...  
from google.cloud import ndb

class BaseModel(ndb.Model):
    _NAMESPACE = ndb.key.UNDEFINED  # Default used to be `None` but no longer works correctly with NDB (change in 1.1.2)

    def __init__(self, **kwds):
        super().__init__(namespace=self._NAMESPACE, **kwds)


class PickleNamespacedOtherKind(BaseModel):
    _NAMESPACE = 'Test'
    foo = ndb.IntegerProperty()

    @classmethod
    def _get_kind(cls):
        return "OtherKind"


class PickleNamespacedSomeKind(BaseModel):
    _NAMESPACE = 'Test'
    other = ndb.StructuredProperty(PickleNamespacedOtherKind)

    @classmethod
    def _get_kind(cls):
        return "SomeKind"



 class TestModel(base.TestNDBModel):
   def setUp(self) -> None:
        self.keys_to_remove =[]
        super().setUp()

    def tearDown(self) -> None:
        for key in self.keys_to_remove:
            key.delete()

        super().tearDown()

    def run(self, result=None):
        from ndb.csg.ndb.client import NDBClient
        self.ndb_client = ndb.Client(project=PROJECT_ID)
        with self.ndb_client.default_context():
            super().run(result)

    def put_entity(self, entity):
        key = entity.put()
        self.keys_to_remove.append(key)
        return key

    def test_namespaced_unsaved_entity_pickling(self):
        """
        Regression test for Issue XXXXXX (redacted)
        """
        entity = PickleNamespacedSomeKind(other=PickleNamespacedOtherKind(foo=1))
        assert entity.other.key is None or entity.other.key.id() is None
        entity = pickle.loads(pickle.dumps(entity))
        assert entity.other.foo == 1

    def test_namespaced_saved_entity_pickling(self):
        """
        Regression test for Issue XXXXXX (redacted)
        """
        entity = PickleNamespacedSomeKind(other=PickleNamespacedOtherKind(foo=1))
        self.put_entity(entity)
        assert entity.other.key is None or entity.other.key.id() is None
        entity = pickle.loads(pickle.dumps(entity))
        assert entity.other.foo == 1

Metadata

Metadata

Assignees

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions