Fenron

Thursday, June 14, 2007

Setting a custom queryset for a ModelChoiceField in init()

Given the following form:


class ListReqStep1(forms.Form):
group = forms.ModelChoiceField(
queryset=IETFWG.objects.all().filter(status=IETFWG.ACTIVE).
select_related(depth=1).order_by('acronym.acronym'),
required=False, empty_label="-- Select Working Group")
domain_name = forms.ChoiceField(choices=DOMAIN_CHOICES,
required=False, widget = forms.Select, initial='ietf.org')
list_to_close = forms.ModelChoiceField(
queryset=ImportedMailingList.objects.all(),
required=False, empty_label="-- Select List To Close")


The list_to_close choices should really depend on the value of the domain_name field (i.e., the lists that are hosted at that domain). The classic way to handle this is to set the choices in __init__(). However, with a ModelChoiceField, there's a twist. The field's choices are a QuerySetIterator that iterates over the queryset when needed (deferring the query until it's definitely needed). Unfortunately, if you just change the form element's queryset in __init__(), that changes what the form field's choices will return, but not what the widget will use. The obvious code:


def __init__(self, *args, **kwargs):
super(ListReqStep1, self).__init__(*args, **kwargs)
# Base the queryset for list_to_close on the initial value
# for the domain_name field.
self.fields['list_to_close'].queryset =
ImportedMailingList.choices(self.initial.get('domain_name','ietf.org'))


simply results in the original queryset being used, since even though you've changed the queryset, the widget has a copy of the original QuerySetIterator. To get the right choices, you have to give them to the widget again - this seems very magic, but works:


def __init__(self, *args, **kwargs):
super(ListReqStep1, self).__init__(*args, **kwargs)
# Base the queryset for list_to_close on the initial value
# for the domain_name field.
self.fields['list_to_close'].queryset =
ImportedMailingList.choices(self.initial.get('domain_name','ietf.org'))
# This is necessary after changing a ModelChoiceField's
# queryset.
self.fields['list_to_close'].widget.choices =
self.fields['list_to_close'].choices

Labels: , ,

0 Comments:

Post a Comment

<< Home