Is it possible to determine with NDB if model is persistent in the datastore or not?

google-app-engine, python

Solution

To get the same kind of state in NDB you would need a combination of post-get-hook and post-put-hook to set a flag. Here's a working example:

class Employee(ndb.Model):
  <properties here>

  saved = False  # class variable provides default value

  @classmethod
  def _post_get_hook(cls, key, future):
    obj = future.get_result()
    if obj is not None:
      # test needed because post_get_hook is called even if get() fails!
      obj.saved = True

  def _post_put_hook(self, future):
    self.saved = True

There's no need to check for the status of the future -- when either hook is called, the future always has a result. This is because the hook is actually a callback on the future. However there is a need to check if its result is None!

PS: Inside a transaction, the hooks get called as soon as the put() call returns; success or failure of the transaction doesn't enter affect them. See https://developers.google.com/appengine/docs/python/ndb/contextclass#Context_call_on_commit for a way to run a hook after a successful commit.

Problem

I am in process of migration from `db.Model` to `ndb.Model`. The only issue that I have to solve before finish this migration is that there is no `Model.is_saved` method. I have used `db.Model.is_saved` in my application to determine if sharded counters must be updated on `put`/`delete`, to check for conflicted keys on creating entities etc. The documentation says that `ndb.Model` has no equivalent for `is_saved` method. I can reimplement some use cases with `get_or_insert` instead of `is_saved`. But not all of them. As a dirty hack I can set flag like `_in_memory_instance` for every instance I have created by calling constructor. But it does not solve my issue. I still have to update this flag at least after every `put()` call. The question is: is there better way to determine if model is persistent in the datastore or not without extra datastore hit? Edit 1: Forgot to mention: all the entities got keys so check for `Model._has_complete_key()` does not work for me. Edit 2: After this discussion https://groups.google.com/d/topic/google-appengine/Tm8NDWIvc70/discussion it seems to be the only way to solve my issue is to use `_post_get_hook`/`_post_put_hook`. I wondering why such a trivial thing was not included in official API. Edit 3: I ended up with next base class for all my models. Now I can leave my codebase (almost) untouched: ``` class BaseModel(ndb.Model): @classmethod def _post_get_hook(cls, key, future): self = future.get_result() if self: self._is_saved = bool(key) def _post_put_hook(self, future): self._is_saved = future.state == future.FINISHING def is_saved(self): if self._has_complete_key(): return getattr(self, "_is_saved", False) return False ```

Original source