views:

97

answers:

3

I'm building a Django site. I need to model many different product categories such as TV, laptops, women's apparel, men's shoes, etc.

Since different product categories have different product attributes, each category has its own separate Model: TV, Laptop, WomensApparel, MensShoes, etc.

And for each Model I created a ModelForm. Hence I have TVForm, LaptopForm, WomensApparelForm, MensShoesForm, etc.

Users can enter product details by selecting a product category through multi-level drop-down boxes. Once a user has selected a product category, I need to display the corresponding product form.

The obvious way to do this is to use a giant if-elif structure:

# category is the product category selected by the user

if category == "TV":
    form = TVForm()
elif category == "Laptop":
    form = LaptopForm()
elif category == "WomensApparel":
    form = WomensApparelForm()
...

Unfortunately there could be hundreds if not more of categories. So the above method is going to be error-prone and tedious.

Is there any way I could use the value of the variable category to directly select and initialize the appropriate ModelForm without resorting to a giant if-elif statement?

Something like:

# This doesn't work

model_form_name = category + "Form"
form = model_form_name()

Is there any way to do this?

+10  A: 

If all your *Form classes are in the one module (let's call it forms), you can do this:

import forms

form = getattr(forms, category + "Form")()

(Obviously, add whatever verification is necessary, such as catching AttributeError. Security-wise, if you are using a named module rather than the global namespace, it's that little bit harder for someone to inject a new *Form class.)

Zooba
I agree with Zooba that you should avoid globals if you can, but if for some reason all your Forms are global, use `form = globals()[category + "Form"]()`
Lee Reeves
+10  A: 

One simple way to do this is to maintain a dictionary of category names to form classes. For e.g.

categories_and_classes = dict(TV = TVForm, Laptop = LaptopForm, ...)

And then you can use the category to look up the form class:

form = categories_and_classes.get(category, DefaultForm)

Alternately you can use convention, as @Zooba said in his answer. This would work if your forms are uniformly named, say <category name> + Form.

Manoj Govindan
+1, that is the only sane way, allows for different form names or having same form for two categories etc etc
Anurag Uniyal
This is certainly the most "correct" way to do it. However, since I can automatically generate the same dictionary from `forms.__dict__`, it is an unnecessary duplication of effort (Don't Repeat Yourself principle).
Zooba
@you can't regenerate the same dict from `forms.__dict__` if different categories map to the same form.
aaronasterling
@AaronMcSmooth You can if you include `TVForm = LaptopForm` in the `forms` module. It really depends how dynamic you need the lookup to be (ie. loaded from an external file/database?).
Zooba
+1  A: 

Sounds like what you need is a mapping, or dictionary in Python. For example, create a dictionary that maps model category names to ModelForm classes. You could have another that maps them to Model classes. Either way you can map the string with the Model name in it to whatever you want.

A more "object-oriented" approach would be to just use the a Model class dictionary and add (possibly static) method(s) to each one which return or do what you need done, such as return the appropriate ModelForm. I mean something like this:

class TV:
    @staticmethod
    def getform():
        return TVForm
...
class Laptop:
    @staticmethod
    def getform():
        return LaptopForm
...
class WomensApparel:
...etc...

Models = { 'TV':TV, 'Laptop':Laptop, 'WomensApparel':WomensApparel, ...etc }

form = Models[category].getform()

With these two techniques, you won't need to write those kinds of giant if-elif structures -- and if you ever start to, it's a sign it's time to re-think your design.

martineau
I don't like the static methods that require the model to know what form gets used on it. a plain dict that maps category to form like @Manoj Govindan uses is simpler.
aaronasterling
@AaronMcSmooth. Good point, but there's nothing that says that `getform()` has to be hardcoded as shown in my simple example. My main point -- obviously not made well -- was that, however it's done, the details should be 'hidden' inside a method or function rather than exhaustively in an `if-elif` structure.
martineau