views:

43

answers:

2

Hi,

in one of my Django models I have to add specific code for each model instance. Now, I wonder what would be a good way to implement this. My current attempt results in a big, hard to read if statement.

Consider the following model:

class Foo(models.Model):
    name = models.CharField(max_length=255)

    def do_instance_specific_stuff(self, arg):
        if self.id == 1:
            do_X(arg)
        elif self.id == 2:
            do_Y(arg)
        else:
            do_Z()

Right now, there is custom code for around 20 model instances and it will stay in this magnitude. Any ideas or patterns how this can be implemented in a clean, readable way?

Thanks for any help.

+1  A: 

The common pattern is to use a dict:

stuff_per_id = {1 : do_X, 2 : do_Y, ...}
def do_instance_specific_stuff(arg):
    try:
        stuff_per_id[self.id](arg)
    except KeyError:
        do_Z()

Though you really shouldn't be doing this. It's much better to put an extra field in your model that signifies what code should be run on arg for this instance. The dict pattern may then still be of use.

Edit: if any of your instance-specific functions may raise KeyError, the above executes do_Z. If you don't want that, add an unused argument to do_Z and use the following:

def do_instance_specific_stuff(arg):
    try:
        f = stuff_per_id[self.id]
    except KeyError:
        f = do_Z
    f(arg)
larsmans
+2  A: 

I would add another field to your model, vendor. Then add per-vendor methods to your model, and invoke them with getattr:

class Foo(models.Model):
    name = models.CharField(max_length=255)
    vendor = models.CharField(max_length=50)

    def vendor_fedex(self, arg):
        blah blah

    def vendor_ups(self, arg):
        blah blah

    def vendor_usps(self, arg):
        blah blah

    def do_instance_specific_stuff(self, arg):
        fn = getattr(self, "vendor_"+self.vendor, None)
        if not fn:
            raise Exception("Uh-oh, bad vendor")
        fn(arg)

Depending on particular values of id seems very fragile. In your data model, each vendor string will appear only once, but it will be readable and malleable. You can decide what you want to do if the getattr can't find the vendor code.

Ned Batchelder