Question

I have two different models that I would like to filter similarly by a common field name at different times, so i've written a single context function that handles both models by taking a string as an argument to use as the model name. Right now I'm using eval(), but something in my gut tells me that's a grave error. Is there a more pythonic way to do what I'm describing?

Here's a shortened version of what my code looks like at the moment:

def reference_context(model, value):
    menu = main_menu()
    info = company_info()
    pages = get_list_or_404(eval(model), category = value)

Secondly, is there a way to pass a keyword in a similar fashion, so I could have something along the lines of:

def reference_context(model, category, value):
    menu = main_menu()
    info = company_info()
    pages = get_list_or_404(eval(model), eval(category) = value)

And commentary on any other issue is welcome and greatly encouraged.

Was it helpful?

Solution

If they are come from the same module (models.py), you can use getattr to retrieve the model class, and kwargs (a dict with double asterisk) this way:

from myapp import models

def reference_context(model, value):
    menu = main_menu()
    info = company_info()
    pages = get_list_or_404(getattr(models, model), **{category: value})

OTHER TIPS

You can use the get_model utility function, which takes the app name and model name.

from django.db.models import get_model
User = get_model("auth", "User") # returns django.contrib.auth.models.User

I don't really see why you need to pass the model as a string - just pass the model reference. E.g.

class ModelA(models.Model):
    ...

class ModelB(models.Model):
    ...

def reference_context(model, **kw):
    menu = main_menu()
    info = company_info()
    pages = get_list_or_404(model, **kw)
    # ...

In this setup you can pass any model and any query you want, e.g.

reference_context(ModelA, category="Hello")

or

reference_context(ModelB, item__ordered__lte=now)

As explained in my comment, if you really need to map strings to models, use an explicit registry/mapping. This prevents people from manipulating form data which might allow them to create a User in stead of, for example, a "Book":

model_map = dict(book=ModelA, magazine=ModelB)
reference_context(model_map[model_as_string], ...)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top