Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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
like image 242
Sergey Avatar asked Aug 23 '12 00:08

Sergey


2 Answers

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.

like image 55
Guido van Rossum Avatar answered Sep 19 '22 14:09

Guido van Rossum


Based on @Tim Hoffmans idea you can you a post hook like so:

class Article(ndb.Model):
    title = ndb.StringProperty()

    is_saved = False

    def _post_put_hook(self, f):
        if f.state == f.FINISHING:
            self.is_saved = True
        else:
            self.is_saved = False


article = Article()
print article.is_saved ## False
article.put()
print article.is_saved ## True

I can't guarantee that it's persisted in the datastore. Didn't find anything about it on google :)

On a side not, looking to see if a ndb.Model instance has a key won't probably work since a new instance seems to get a Key before it's ever sent to the datastore. You can look at the source code to see what happens when you create an instance of the ndb.Model class.

like image 39
fredrik Avatar answered Sep 21 '22 14:09

fredrik