Sergey
Sergey

Reputation: 647

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

Upvotes: 17

Views: 4667

Answers (3)

shivamag00
shivamag00

Reputation: 481

If you do not mention a key while creating an instance of the Model, you can use the following implementation of is_saved() to know if the object has been written to the datastore or not atleast once. (should be appropriate if you are migrating from google.appengine.ext.db to google.appengine.ext.ndb)

Using the example given by @fredrik,

class Article(ndb.Model):
    title = ndb.StringProperty()
    
    def is_saved(self):
        if self.key:
            return True
        return False

P.S. - I do not know if this would work with google.cloud.ndb

Upvotes: 0

Guido van Rossum
Guido van Rossum

Reputation: 16890

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.

Upvotes: 14

fredrik
fredrik

Reputation: 17617

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.

Upvotes: 2

Related Questions