Eric P
Eric P

Reputation: 4657

Override ChoiceField widget in Django ModelForm without re-specifying choices

I am trying to override the default widget for a Django ChoiceField while preserving the choices generated in the model. Here is the relevant part of my model:

class UserGroup(models.Model):
    icon = models.CharField(max_length=100, choices=<function call>)

And of my form:

class UserGroupForm(forms.ModelForm):
    icon = models.ChoiceField(widget=IconPicker)

    class Meta:
        model = UserGroup
        fields = [ 'icon', ]

Overriding the ChoiceField's widget like this clobbers the form.fields['icon'].choices attribute normally inherited from the model and sets it to [] because Django. If I remove the icon field definition from the form, the choices are preserved - but of course the widget defaults to a Select.

(The function which generates the choices for the model field is not accessible from the form code unfortunately.)

The best I have come up with so far for is to change the icon form field definition to

icon = ChoiceField(choices=UserGroup._meta.get_field_by_name('icon')[0].choices, 
                   widget=IconPicker)

but this is clunky and I would rather have the choices automatically passed on as in the introspected ChoiceField behavior. (I tried subclassing ChoiceField to IconChoiceField which was identical but for a default widget of IconPicker but Django converts it back to a TypedChoiceField with the default Select widget because of this issue.)

Is there a way to override the ChoiceField's widget attribute while preserving the behavior of inheriting choices from the model?

Upvotes: 4

Views: 7477

Answers (2)

Tom
Tom

Reputation: 2016

I think the reason you are losing the choices you specified in your model i.e. "sets it to []" is not "because Django", but because you override the icon field with the line icon = models.ChoiceField(widget=IconPicker)

Please note that if you are using a modelform then it is not necessary to override the widget at init, instead the widget should be specified in the Meta class in the widgets dictionary.

class UserGroupForm(forms.ModelForm):

    class Meta:
        model = UserGroup
        fields = [ 'icon', ]
        widgets = {
            'icon': IconPicker
        }

As for overriding the choices you can simply do self.fields['icon'].choices = UserGroupForm.ICON_CHOICES, but I don't think you need to override choices in this instance.

Upvotes: 7

Eric P
Eric P

Reputation: 4657

Figured it out. Just needed a bit of self-reference in UserGroupForm's __init__:

def __init__(self, *args, **kwargs):
    super(UserGroupForm, self).__init__(*args, **kwargs)
    self.fields['icon'].widget = IconPicker(choices=self.fields['icon'].choices)

Upvotes: 5

Related Questions