views:

33

answers:

1

What are the applications for Google App Engine Expando Class? And what are the good practices related to it?

+1  A: 

Two common uses of Expandos are partially-fixed schemas and deleting old properties.

I frequently use Expando when I have a kind that needs slightly different properties across entities; in other words, when I need a 'partially' dynamic schema. One use-cases is an application that takes orders where some products are liquid (think water), some are physical units (think DVDs), and some are 'other' (think flour). Some fields, like item code, price and quantity, are always needed. But, what if the details of how quantity was computed is also needed?

Typically a fixed-schema solution would be to add a property for all of the variables we might use: weight, dimension, before and after weights of our stock, and so on. That sucks. For every entity most of the other fields are not needed.

class Order(db.Model):
    # These fields are always needed.
    item_code = db.StringProperty()
    unit_of_measure = db.StringProperty()
    unit_price = db.FloatProperty()
    quantity = db.FloatProperty()

    # These fields are used depending on the unit of measure.
    weight = db.FloatProperty()
    volume = db.FloatProperty()
    stock_start_weight = db.FloatProperty()
    stock_end_weight = db.FloatProperty()

With Expando we can do much better. We could use the unit_of_measure to tell us how we computed quantity. The functions that compute quantity can set the dynamic fields, and the functions that read that method's information know what to look for. And, the entity does not have a bunch of unneeded properties.

class Order(db.Expando):
    # Every instance has these fields.
    item_code = db.StringProperty()
    unit_of_measure = db.StringProperty()
    unit_price = db.FloatProperty()
    quantity = db.FloatProperty()


def compute_gallons(entity, kilograms, kg_per_gallon):
    # Set the fixed fields.
    entity.unit_of_measure = 'GAL'
    entity.quantity = kilograms / kg_per_gallon

    # Set the gallon specific fields:
    entity.weight = kilograms
    entity.density = kg_per_gallon

You could achieve a similar result by using a text or blob property and serializing a dict of 'other' value to it. Expando basically 'automates' that for you.

Robert Kluin
Another option in the situation you outline is to use PolyModel, instead.
Nick Johnson
You can; but you might wind up creating and importing tons of models to handle variations (for this type of example). I deal with liquids, and there are a *lot* (our app uses about 12) of ways to compute volume.
Robert Kluin