I'm writing a REST api over Google App Engine NDB
I excluded existing libraries because I need control over transactions and caching. I excluded Google Endpoints for a similar reason and also because I don't want to use the javascript client they provide.
When evaluating architectural decisions I encountered some problems and weird situations, this is because my experience with python and pythonic style is probably not big enough.
In this moment I tried to come up with some guidelines that should shape my codebase:
- in Handlers -> create the dictionary representation of the objects
and return them as json; perform authentication and authorization
checks - encapsulate ndb interaction in Services
- Model classes always receive Model objects or keys as parameters and return Model objects or Model lists
- Model classes are imported and used in services
One particular thing I encountered is this I have a many to many relationship that I implemented with a mapping Model, something like UserOrganizationMembership, that has the keys of the User and the Organization
now, in my Service, at some point I want to return an object that has the list of the organizations the current user is member of and recursively fetch the companies that are in each organization:
'organizations': [
{
'name': 'TEST'
'companies': [ {company1}, {company2}]
},
{
...
}
]
I do this
def user_organizatios_with_companies(user):
def fetch_companies(x):
x.companies = Company.by_organization(x) #NOTICE THIS
return x
user_organizations = [ membership.organization.get() for membership in UserOrganizationMembership.by_user(user)]
return [fetch_companies(x) for x in user_organizations]
in the highlighted line I attach the dynamic property 'companies' to the Organization Model
now if I call this method in my Handler, when I create the dictionary representation to output json, ndb.Model.to_dict() implementation ignores dynamically attached properties.
One solution I tried is this (in my Handler)
xs = Service.user_organizatios_with_companies(u)
organizations = [x.to_dict() for x in xs]
for x in xrange(0,len(xs)):
organizations[x]['companies'] = xs[x].to_dict()
but I don't like it because I need to know that each organization has a 'companies' property and the code seems a bit complicated and not obvious
another approach is to override ndb.Model.to_dict() isolating dynamically attached properties and providing a dictionary representation, this simplifies my code in the Handler letting me to only call to_dict() on the stuff returned by the Service.
from google.appengine.ext import ndb import util
class BaseModel(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add = True)
updated = ndb.DateTimeProperty(auto_now = True)
# App Engine clock times are always
# expressed in coordinated universal time (UTC).s
def to_dict(self, include=None, exclude=None):
result = super(BaseModel,self).to_dict(include=include, exclude=exclude)
result['key'] = self.key.id() #get the key as a string
# add properties dynamically added to the class
dynamic_vars = {k:v for (k,v) in vars(self).iteritems() if not k.startswith('_')}
for prop, val in dynamic_vars.iteritems():
result[prop] = val.to_dict() if not isinstance(val, list) else [x.to_dict() for x in val]
return util.model_db_to_object(result)
do you have any recommendation against this approach? any thought will be appreciated!