rdodev
rdodev

Reputation: 3202

AppEngine Model/Handler Questions

Our team is relatively new to AppEngine and we're still learning the ropes. We're using plain ndb.Model models (i.e. nothing fancy) and webapp2 handlers. The app is UI-less since it's just a restful API.

So we have a model defined as such:

from google.appengine.ext import ndb
class Pasta(ndb.Model):
 type = ndb.StringProperty(indexed=True)
 name = ndb.StringProperty()
 contents = ndb.JsonProperty()
 modified_date = ndb.DateTimeProperty(auto_now=True)
 added_date = ndb.DateTimeProperty(auto_now_add=True)

And the use case is that we would like to add a record if one doesn't exist and if it does exist, return it. We could query for it in the handler and even create one there and use the pasta.put() instance method to create a new one. However, we think that data-bound code belongs in the model, not the handler, but, if we're not mistaken, in the model context we need to use the get_or_insert() which requires we explicitly declare a Key, correct?

Any suggestions how to handle this sort of logic in the model? TIA.

Upvotes: 1

Views: 137

Answers (3)

stevep
stevep

Reputation: 959

Key access is faster and less expensive than a query. Very good idea to always determine that you cannot use IDs before implementing a query. Also, remember that the default index state for all model properties is True, so whenever setting up your model it is a very good idea to always state indexed=True/False. Many cases where folks forget that the default is True, and then wonder why their index storage gets very large when they are only querying on a small number of properties. Did you want indices on your other model properties??

Upvotes: 0

dragonx
dragonx

Reputation: 15143

You have a very odd argument for why creating an instance (or record) belongs in the model.

Yes the model represents the data, but the handler is the one responsible for creating instances of your model. You don't provide enough information on what your handler does.

First, you'll need to determine what you want to use as your key. If you're just using the ndb randomly created keys, i don't know how you'd determine if the record exists. I'm assuming you'd want to use either Pasta.type or Pasta.name as the key.

In this case, the 'type' or 'name' would likely be provided as parameters to your handler, and your handler would call get_or_insert after constructing the key from its parameters.

I could also interepret your question as saying you only want a single record for the given Pasta model. That would be fairly atypical datastore usage, but in that case it might make sense to have method in your model to check whether the single instance exists. In that case you can hard-code your key.

Upvotes: 0

Daniel Roseman
Daniel Roseman

Reputation: 600041

I don't know why you think the decision on whether the code goes in the model or the handler determines the method you use to do the query. You can use get_or_insert in either place, but it does mean that you need to know the key of the entity you want. And, you can do queries in either place.

If you do want to keep all your data interaction within the model, one possible pattern is to define a classmethod on the model that does the query and returns either the existing instance or a new one. Something like:

class Pasta(ndb.Model): 
    ...
    @classmethod
    def get_or_create(cls, type=None, name=None):
        query = cls.query(cls.type=type, cls.name=name)
        existing = query.get()
        if existing:
            return existing
        else:
            new_item = cls(type=type, name=name)
            new_item.put()
            return new_item

(Note I haven't actually used ndb, but this is more or less right.) You'll want to make sure this method is called within a transaction.

Upvotes: 1

Related Questions