Search code examples
pythondjangodjango-formsextra

Specifying widget for model form extra field (Django)


I have to add extra field to my model form. My approach is:

class MyForm(forms.ModelForm):
    extra_field = forms.CharField()
    class Meta:
        model = MyModel
        widgets = {
            #Does not work
            'extra_field': forms.Textarea(attrs={'placeholder': u'Bla bla'}),
        }

But it seems that widget definition for extra_field at class Meta is ignored, because i have a bare input tag instead of textarea on a template. So I apply next approach:

class MyForm(forms.ModelForm):
    #It works fine
    extra_field = forms.CharField(widget=forms.Textarea())
    class Meta:
        model = MyModel

It works perfectly for me, but I used to specify widgets for form fields at class Meta declaration. So I wonder:

Why my first approach doesn't work? What I am doing wrong?


Solution

  • It doesn't matter if it's an extra field. This works:

    class FooForm(forms.ModelForm):
        class Meta:
            model = People
            widgets = { 
                'name': forms.Textarea(attrs={'placeholder': u'Bla bla'}),
            }   
    

    This doesn't:

    class FooForm(forms.ModelForm):
        name = forms.CharField()
    
        class Meta:
            model = People
            widgets = { 
                'name': forms.Textarea(attrs={'placeholder': u'Bla bla'}),
            }
    

    This is not documented indeed, that's the best I could find in the docs that could relate to that behaviour (maybe it doesn't, it's just the best i could find):

    If you explicitly instantiate a form field like this, Django assumes that you want to completely define its behavior [...] you must set the relevant arguments explicitly when declaring the form field.

    The implementation of this behaviour is in django/forms/models.py line 219:

       204         if opts.model:
       205             # If a model is defined, extract form fields from it.
       206             fields = fields_for_model(opts.model, opts.fields,
       207                                       opts.exclude, opts.widgets, formfield_callback)
       208             # make sure opts.fields doesn't specify an invalid field
       209             none_model_fields = [k for k, v in fields.iteritems() if not v]
       210             missing_fields = set(none_model_fields) - \
    EE 211                              set(declared_fields.keys())
       212             if missing_fields:
       213                 message = 'Unknown field(s) (%s) specified for %s'
       214                 message = message % (', '.join(missing_fields),
       215                                      opts.model.__name__)
       216                 raise FieldError(message)
       217             # Override default model fields with any custom declared ones
       218             # (plus, include all the other declared fields).
       219             fields.update(declared_fields)
    

    After line 206, fields['name'].widget is indeed the Textarea specified in Meta.widgets.

    At line 219, fields is updated with declared_fields, and fields['name'].widget becomes django.forms.widgets.TextInput which is the default for CharField.

    Apparently, explicit field definitions have priority.

    Thanks for asking, good to know, great question.