philgo20
philgo20

Reputation: 6517

Dynamic choices for Django SelectMultiple Widget

I'm building a form (not modelForm) where i'd like to use the SelectMultiple Widget to display choices based on a query done during the init of the form.

I can think of a few way to do this but I am not exactly clear on the right way to do it. I see different options.

I get the "choices" I should pass to the widget in the form init but I am not sure how I should pass them.

class NavigatorExportForm(forms.Form):

def __init__(self,user, app_id, *args,**kwargs):
    super (NavigatorExportForm,self ).__init__(*args,**kwargs) # populates the form
    language_choices = Navigator.admin_objects.get(id=app_id).languages.all().values_list('language', flat=True)

languages = forms.CharField(max_length=2, widget=forms.SelectMultiple(choices=???language_choices))

Upvotes: 4

Views: 3451

Answers (2)

Clément
Clément

Reputation: 6748

Why not use a ModelMultipleChoiceField instead?

You could do simply this :

class NavigatorExportForm(forms.Form):
    languages = forms.ModelMultipleChoiceField(queryset=Language.objects.all())

    def __init__(self, app_id, *args, **kwargs):
        super(NavigatorExportForm, self).__init__(*args, **kwargs)
        # Dynamically refine the queryset for the field
        self.fields['languages'].queryset = Navigator.admin_objects.get(id=app_id).languages.all()

This way you don't only restrict the choices available on the widget, but also on the field (that gives you data validation).

With this method, the displayed string in the widget would be the result of the __unicode__ method on a Language object. If it's not what you want, you could write the following custom field, as documented in ModelChoiceField reference :

class LanguageMultipleChoiceField(forms.ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        return obj.language_code # for example, depending on your model

and use this class instead of ModelMultipleChoiceField in your form.

Upvotes: 6

philgo20
philgo20

Reputation: 6517

def __init__(self,user, app_id, *args,**kwargs):
super (NavigatorExportForm,self ).__init__(*args,**kwargs)
self.fields['languages'].widget.choices = Navigator.admin_objects.get(id=app_id).languages.all().values_list('language', flat=True)

that seems to do the trick, but even by not specifying a max_length, the widget only display the first letter of the choices...

Upvotes: 0

Related Questions