When to maintain reference to key vs. reference to actual entity object after put operation.
Question: When working with datastore entities in App Engine, people have noticed odd
behavior after a put operation is performed on an entity if you choose to hold
on to a reference of that entity.

For example, see [this issue](https://code.google.com/p/appengine-ndb-
experiment/issues/detail?id=208) where repeated String properties mutated to
_BaseValue after a put was performed.

In the ensuing discussion, in reference to a repeated String property, Guido
van Rossum writes:

> "I see. We should probably document that you're not supposed to hang on to
> the list of items for too long; there are various forms of undefined
> behavior around that."

I get the sense from this thread that it's not a good idea to maintain
reference to an entity for too long after a put, as unexpected behavior might
arise.

However, when I look at the [GAE source
code](https://code.google.com/p/googleappengine/source/browse/branches/1.2.1/python/google/appengine/ext/db/__init__.py)
for the `Model.get_or_insert()` method, I see the following code (docstring
removed):

    
    
      @classmethod
      def get_or_insert(cls, key_name, **kwds):
        def txn():
          entity = cls.get_by_key_name(key_name, parent=kwds.get('parent'))
          if entity is None:
            entity = cls(key_name=key_name, **kwds)
            entity.put()
          return entity
        return run_in_transaction(txn)
    

Notice how a put operation is performed, and the entity is returned post-put.
Just above, we saw an example of when this is not recommended.

Can anyone clarify when and when it is not ok to maintain a reference to an
entity, post put? Guido seemed to hint that there are various scenarios when
this could be a bad idea. Just curious if anyone has seen documentation on
this (I cannot find any).

Thanks!


Answer: The problem described in the issue is not regarding entities, but rather lists
obtained from its properties. You can hold a copy of entity as long as you
like. It's just an object.

The issue is caused by some "magical" functionality provided by ndb. Let's
take a look at the model definition

    
    
    from google.appengine.ext.ndb import model
    
    class MyModel(model.Model):
        items = model.StringProperty(repeated=True)
    

What can we say about items property?

It looks like a class attribute, but metaclass logic of model.Model transforms
it into an instance attribute.

What type are these instance attributes?

They can be accessed like a list of strings, but they are more complex objects
having the logic required for storing and retrieving the data from datastore,
validating etc.

This "magic" works well in most cases, but sometimes it doesn't. One of the
problematic cases is when you get the reference to items from the instance and
try to use it after put was called. Another case, mentioned by Guido, was to
pass external list to initialize items property and then try to modify this
property by manipulating the external list.

**The thing to remember** : Model properties in ndb try to behave like their
basic types, but they are more complex objects. You can read more about their
internals in [Writing property
subclasses](https://cloud.google.com/appengine/docs/python/ndb/subclassprop)

