Lost in LoC


Google AppEngine PolyModels and ModelForms

Posted in Development,Technology by Ryan Baldwin on April 11, 2009
Tags: , , ,

I recently created my first PolyModel class in Google AppEngine for a new feature. The feature was pretty straight forward  - allow comments for some of the entities in our application Homebook.  We wanted each comment to be tied back to its target entity – a room, house, or image – in a 1-to-many (each entity can have many comments). Using PolyModel we found this to be pretty easy without duplicating a bunch of code.  Essentially the structure of Comment class hierarchy looked like the following:

class Comment(PolyModel):
    # bunch of common fields

class RoomComment(Comment):
    target = db.ReferenceProperty(reference_class=Room, collection_name=’comments’)

class HomeComment(Comment):
    target = db.ReferenceProperty(reference_class=Home, collection_name=’comments’)

class ImageComment(Comment):
    target = db.ReferenceProperty(reference_class=Image, collection_name=’comments’)

Really straight forward – simple class inheritance. I then created a simple factory method that would fetch the proper class type for a given instance of one of our commentable entities. Things were moving along nicely (complete with lots of beautiful unit tests – you do write unit tests, right?) and it was time to create the add_comment view.

For the most part we use Google AppEngine’s djangoforms ModelForms, a theoretically clean and easy way to create web forms based on data models. I created my standard ModelForm class (based on our parent Comment class) and the view against which the form would post, only to be blindsided by the oddest of validation errors:

  • _class
    • This field is required.

Huh? _class? What gives? Well, after a couple hours of soul searching, googling (which turned up nothing), and a few tears, my genius coworker Tony Arkles had an epiphany: The PolyModel class has a _class field which is used to keep track of the PolyModel instance’s class hierarchy. We added the _class field to our form’s exclude list and the world righted itself, cats and dogs learned to coexist, and Liz Taylor finally discovered a happy marriage.

Now the question is, why is the ModelForm rendering a field that’s not a google.appengine.ext.db model property? Can anybody from Google answer me that?