1

Given the example below, is there a more elegant solution than passing a string to allow a function to test against and in turn, run the required code?

myfunction(self, "location1")

def myfunction(self, source):
    if source == "location1":
        qs = MyModel.objects.filter(id = self.object.id)
        return qs
    elif source == "location2":
        qs = AnotherModel.objects.filter(id = self.object.id)
        return qs
    else:
        qs = YetAnotherModel.objects.filter(id = self.object.id)
        return qs

This example contains dummy Django queries, but I've had to use this solution on various Python functions throughout my projects.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Karl
  • 733
  • 1
  • 14
  • 28
  • Why not create three separate functions and simply call them in each respective location? Or (if you really want one function) - pass at object which is actually needed, in this case `MyModel`, `AnotherModel` or `YetAnotherModel`. – Łukasz Rogalski Nov 20 '15 at 13:03
  • You probably want the [Strategy Pattern](http://stackoverflow.com/questions/963965/how-is-this-strategy-pattern-written-in-python-the-sample-in-wikipedia) or possibly the [State Pattern](http://stackoverflow.com/questions/2101961/python-state-machine-design) – Peter Wood Nov 20 '15 at 13:04
  • Multiple functions sounds good. Maybe I was trying to be too DRY. Thanks for the comments. – Karl Nov 20 '15 at 13:10
  • Possible duplicate of [Most efficient way of making an if-elif-elif-else statement when the else is done the most?](http://stackoverflow.com/questions/17166074/most-efficient-way-of-making-an-if-elif-elif-else-statement-when-the-else-is-don) – kramer65 Nov 20 '15 at 13:31

2 Answers2

2

I think this way is cleaner:

def myfunction(self, source):
    models = { "location1": MyModel,
               "location2": AnotherModel,
               "location3": YetAnotherModel
             }
    selected = models.get(source, YetAnotherModel)
    qs = selected.objects.filter(id = self.object.id)
    return qs
Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
Iman Mirzadeh
  • 12,710
  • 2
  • 40
  • 44
0

I think this one may be ok:

from collections import defaultdict
def myfunction(source):
    return defaultdict(lambda: YetAnotherModel.objects.filter(id = self.object.id),
           { "location1": MyModel.objects.filter(id = self.object.id),
             "location2": AnotherModel.objects.filter(id = self.object.id) })
           [source]

Hope this helps!

  • 1
    This is not pythonic at all. Im struggling to figure out what is it doing. – dopstar Nov 20 '15 at 13:20
  • @dopstar it's just a dictionary with a default value. It's keys are strings and it values are just functions. It's not so difficult to undestand the code. – Damián Montenegro Nov 20 '15 at 13:27
  • 1
    Of course you understand it because you did it. It the code looks too much on the face. The import makes it as if there is not elegant way to do without it, the lambda adds more fuel to the fire. Then you have the normal dict too and wait, there is that awkward `[source]`. Its also inefficient because you are actually calling all the models which is not ideal at all. For each 'source' you will call all models. If they are changing something over an API request that will be very very slow and might be dangerous thing to do as well. – dopstar Nov 20 '15 at 21:22