2

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.

3
  • 3
    This is a terrible idea. Why do you think you need it? Commented Oct 20, 2010 at 8:21
  • @Ignacio Vazquez-Abrams: the model in question stores vendor instances and each vendor has specific rules on how to contact some of their services. This is nothing I can store in a model field, I have to execute different code for each model instance. I'm glad for any hints how this can or should be implemented. Commented Oct 20, 2010 at 8:45
  • You really have multiple subclasses (one per vendor). You never have "instance-specific" features. What you have is "model-specific" features and very, very few instances of the model. Typically only one. We do this, also. Commented Oct 20, 2010 at 11:01

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.