Django dynamic choices
I am wanting to pull a filtered list开发者_JAVA百科 of models classes for a CharField choices. I understand the choices can be any iterable, as long as it contains 2 element tuples.
Code goes like this:
WORKFLOWAWARE_MODELS = [(m.__name__, m.__name__) for m in models.get_models() if 'WorkflowAware' in [b.__name__ for b in m.__bases__]]
class Workflow(models.Model):
""" Workflow controls who does what where """
workflow_content_type = models.CharField(max_length=64, choices=WORKFLOWAWARE_MODELS, unique=True, blank=True)
The list comprehension returns a list of 2 string tuples like this:
>>> x = [(m.__name__, m.__name__) for m in models.get_models() if 'WorkflowAware' in [b.__name__ for b in m.__bases__]] >>> x [('ActivityContent', 'ActivityContent')] >>> x.__class__ type 'list' >>> x[0].__class__ type 'tuple' >>> x[0][0].__class__ type 'str'
All I get in the admin interface is a standard CharField rendering, no choices.
When I cut and paste the value returned by the list comprehension the admin renders the choices:
WORKFLOWAWARE_MODELS = [('ActivityContent', 'ActivityContent')]
What am I missing??
PS tuple() makes no difference.
The problem is that the WORKFLOWAWARE_MODELS value may (and probably will) get evaluated before the correct context is initialized. E.g. during importing this module, ActivityContent and ActivityContent are not yet loaded. What you need is to make WORKFLOWAWARE_MODELS a callable, e.g. by using lambda. Something like this:
WORKFLOWAWARE_MODELS = lambda: [(m.__name__, m.__name__) for m in models.get_models() if 'WorkflowAware' in [b.__name__ for b in m.__bases__]]
This way it gets evaluated every time WORKFLOWAWARE_MODELS is called.
class WorkflowForm(forms.ModelForm):
content_type = forms.ChoiceField(choices=())
def __init__(self, *args, **kwargs):
super(WorkflowForm, self).__init__(*args, **kwargs)
self.fields['content_type'].choices = [('', '---')] + WORKFLOWAWARE_MODELS()
精彩评论