views:

276

answers:

2

When I run Google App Engine likeso:

 from google.appengine.ext import db
 from google.appengine.ext.db import polymodel

 class Father(polymodel.PolyModel):
      def hello(self):
          print "Father says hi"

 class Son(Father):
      def hello(self):
          print "Spawn says hi"

When I run, e.g.

 s = Son()
 s.put()

 son_from_father = Father.get_by_id(s.key().id())

 son_from_father.hello()

This prints "Father says hi". I would expect this to print "Son says hi". Does anyone know how to make this do what's expected, here?

EDIT:

The problem was, ultimately, that I was saving Spawn objects as Father objects. GAE was happy to do even though the Father objects (in my application) have fewer properties. GAE didn't complain because I (silently) removed any values not in Model.properties() from the data being saved.

I've fixed the improper type saving and added a check for extra values not being saved (which was helpfully a TODO comment right where that check should happen). The check I do for data when saving is basically:

def save_obj(obj, data, Model):
   for prop in Model.properties(): # checks/other things happen in this loop
      setattr(obj, prop, data.get(prop))

   extra_data = set(data).difference(Model.properties())
   if extra_data:
      logging.debug("Extra data!")

The posts here were helpful - thank you. GAE is working as expected, now that I'm using it as directed. :)

A: 

You did a "Father.get..." so you created an object from the Father class. So why wouldn't it say "Father says hi".

If you Father class had lastname and firstname, and your Son class had middle name, you won't get the middle name unless you specifically retrieve the 'Son' record.

If you want to do a polymorphic type query, here's one way to do it. I know it works with attributes, but haven't tried it with methods.

 fatherList = Father.all().fetch(1000) 
 counter = 0 
 #I'm using lower case father for object and upper case Father for your class...
 for father in fatherList:
     counter += 1 
     if isinstance(father,Son):
        self.response.out.write("display a Son field or do a Son method") 
     if isinstance(father,Daughter):
        self.response.out.write("display a Daughter field or do a Daughter method")

Neal Walters

NealWalters
The polymodel class adds polymorphism to models, so that Father.get() will return a subclass instance, as appropriate.
Nick Johnson
In my world though, the subclasses all have different attributes. And I don't have any common methods. So I have to see what kind of thing it is before handling it.
NealWalters
+1  A: 

I can't reproduce your problem -- indeed, your code just dies with an import error (PolyModel is not in module db) on my GAE (version 1.2.5). Once I've fixed things enough to let the code run...:

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.db import polymodel

class Father(polymodel.PolyModel):
    def hello(self):
        return "Father says hi"

class Son(Father):
    def hello(self):
        return "Spawn says hi"

class MainHandler(webapp.RequestHandler):

  def get(self):
    s = Son()
    s.put()
    son_from_father = Father.get_by_id(s.key().id())
    x = son_from_father.hello()
    self.response.out.write(x)

def main():
  application = webapp.WSGIApplication([('/', MainHandler)],
                                       debug=True)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()

...I see "Spawn says hi" as expected. What App Engine release do you have? What happen if you use exactly the code I'm giving?

Alex Martelli
Alex also added (self) to the second "def hello()" method.
NealWalters
One difference between your implementation and the asker's is that you've (correctly) defined the 'hello' method on Son as taking a self argument. I suspect there's more we're not seeing, since the demo doesn't run.
Nick Johnson
Thanks for the responses. Yes, this is just an excerpt from more complex code -- I inadvertently left out the self arg to Spawn's hello and that the incorrect import. However, presuming that I hadn't mangled the code when I copied it over :), something's still amiss. I'll try out this example and see what's happening.
Brian M. Hunt
The answer was to assume that GAE was working as expected, and challenge the assumptions made elsewhere.
Brian M. Hunt