views:

147

answers:

2

I'm getting back to programming for Google App Engine and I've found, in old, unused code, instances in which I wrote constructors for models. It seems like a good idea, but there's no mention of it online and I can't test to see if it works. Here's a contrived example, with no error-checking, etc.:

class Dog(db.Model):
    name = db.StringProperty(required=True)
    breeds = db.StringListProperty()
    age = db.IntegerProperty(default=0)

    def __init__(self, name, breed_list, **kwargs):
        db.Model.__init__(**kwargs)
        self.name = name
        self.breeds = breed_list.split()

rufus = Dog('Rufus', 'spaniel terrier labrador')
rufus.put()

The **kwargs are passed on to the Model constructor in case the model is constructed with a specified parent or key_name, or in case other properties (like age) are specified. This constructor differs from the default in that it requires that a name and breed_list be specified (although it can't ensure that they're strings), and it parses breed_list in a way that the default constructor could not.

Is this a legitimate form of instantiation, or should I just use functions or static/class methods? And if it works, why aren't custom constructors used more often?

+1  A: 

There's usually no need to do something like that; the default constructor will assign name, and when working with a list it almost always makes more sense to pass an actual list instead of a space-separated string (just imagine the fun if you passed "cocker spaniel" instead of just "spaniel" there, for one thing...).

That said, if you really need to do computation when instantiating a Model subclass instance, there's probably nothing inherently wrong with it. I think most people probably prefer to get the data into the right form and then create the entity, which is why you're not seeing a lot of examples like that.

Wooble
+1: Sticking to convention is a good reason.
Nikhil Chelliah
+1  A: 

In your example, why not use the default syntax instead of a custom constructor:

rufus = Dog( name='Rufus', breeds=['spaniel','terrier','labrador'] )

Your version makes it less clear semantically IMHO.

As for overriding Model constructors, Google recommends against it (see for example: http://groups.google.com/group/google-appengine/browse_thread/thread/9a651f6f58875bfe/111b975da1b4b4db?lnk=gst&q=python+constructors#111b975da1b4b4db) and that's why we don't see it in Google's code. I think it's unfortunate because constructor overriding can be useful in some cases, like creating a temporary property.

One problem I know of is with Expando, anything you define in the constructor gets auto-serialized in the protocol buffer. But for base Models I am not sure what are the risks, and I too would be happy to learn more.

Julian Namaro
Okay, so retrieving models from the datastore, GAE passes values directly via **kwargs and expects no mutation? I see the problem.
Nikhil Chelliah
If GAE calls the *super* constructor, then you could probably just search for models in the datastore by using the default syntax, as you showed in your post. But if GAE calls the custom constructor, and if it does so using its own **kwargs, then those are going to be intercepted and messed up.
Nikhil Chelliah
Down the road I may experiment to see which constructor is called when retrieving models.
Nikhil Chelliah
Sorry my post wasn't clear, but the syntax I gave was to show that you don't need a custom constructor in your example. That said I agree in some other cases it could be useful.
Julian Namaro