60

How can I change the width of a textarea form element if I used ModelForm to create it?

Here is my product class:

class ProductForm(ModelForm):
    long_desc = forms.CharField(widget=forms.Textarea)
    short_desc = forms.CharField(widget=forms.Textarea)
    class Meta:
        model = Product

And the template code...

{% for f in form %}
    {{ f.name }}:{{ f }}
{% endfor %}

f is the actual form element...

Vini.g.fer
  • 11,639
  • 16
  • 61
  • 90
Josh Hunt
  • 14,225
  • 26
  • 79
  • 98

5 Answers5

128

The easiest way for your use case is to use CSS. It's a language meant for defining presentation. Look at the code generated by form, take note of the ids for fields that interest you, and change appearance of these fields through CSS.

Example for long_desc field in your ProductForm (when your form does not have a custom prefix):

#id_long_desc {
    width: 300px;
    height: 200px;
}

Second approach is to pass the attrs keyword to your widget constructor.

class ProductForm(ModelForm):
    long_desc = forms.CharField(widget=forms.Textarea(attrs={'cols': 10, 'rows': 20}))
    short_desc = forms.CharField(widget=forms.Textarea)
    class Meta:
        model = Product

It's described in Django documentation.

Third approach is to leave the nice declarative interface of newforms for a while and set your widget attributes in custom constructor.

class ProductForm(ModelForm):
    long_desc = forms.CharField(widget=forms.Textarea)
    short_desc = forms.CharField(widget=forms.Textarea)
    class Meta:
        model = Product

    # Edit by bryan
    def __init__(self, *args, **kwargs):
        super(ProductForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
        self.fields['long_desc'].widget.attrs['cols'] = 10
        self.fields['long_desc'].widget.attrs['rows'] = 20

This approach has the following advantages:

  • You can define widget attributes for fields that are generated automatically from your model without redefining whole fields.
  • It doesn't depend on the prefix of your form.
gypaetus
  • 6,873
  • 3
  • 35
  • 45
zuber
  • 3,449
  • 2
  • 24
  • 19
  • 2
    Option 3 is very useful. Perhaps the example could also show that the fields do not need to be defined in the Form, yet can still override fields defined in the Model that come through automatically. – Dan Breen Dec 03 '09 at 16:20
18

Excellent answer by zuber, but I believe there's an error in the example code for the third approach. The constructor should be:

def __init__(self, *args, **kwargs):
    super(ProductForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
    self.fields['long_desc'].widget.attrs['cols'] = 10
    self.fields['long_desc'].widget.attrs['cols'] = 20

The Field objects have no 'attrs' attributes, but their widgets do.

bryan
  • 2,223
  • 1
  • 22
  • 19
  • 1
    That really should be an edit, rather than its own answer (its better with the SO model), but you dont have the sufficient reputation to make edits... – Josh Hunt Mar 13 '09 at 09:30
  • 1
    I have merged the edit in, we can keep this answer also if someone wants to vote it up. – Ólafur Waage Mar 13 '09 at 10:23
  • 3
    Sorry, you're right -- I should have left a comment instead. I'll do better next time :) – bryan Mar 13 '09 at 23:58
15

In the event that you're using an add-on like Grappelli that makes heavy use of styles, you may find that any overridden row and col attributes get ignored because of CSS selectors acting on your widget. This could happen when using zuber's excellent Second or Third approach above.

In this case, simply use the First Approach blended with either the Second or Third Approach by setting a 'style' attribute instead of the 'rows' and 'cols' attributes.

Here's an example modifying init in the Third Approach above:

def __init__(self, *args, **kwargs):
    super(ProductForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
    self.fields['short_desc'].widget.attrs['style'] = 'width:400px; height:40px;'
    self.fields['long_desc'].widget.attrs['style']  = 'width:800px; height:80px;'
bergdesign
  • 846
  • 1
  • 7
  • 6
  • You can also add css classes in the admin.py for the ModelAdmin, I think that will be a bit cleaner. A bit disappointed that Grappelli doesn't use the cols/rows that stock django admin provides. – radtek Jan 12 '15 at 16:47
  • Is there any way to apply a specific `width` to all of the fields in the form with one line of code and not explicitly applying it to them one by one? – Ibo Aug 21 '18 at 22:01
  • 1
    i was able to customize the default look of my django login form using this approach – Ariful Haque May 19 '21 at 13:29
1

Set row and your css class in your admin model view:

'explicacion': AutosizedTextarea(attrs={'rows': 5, 'class': 'input-xxlarge', 'style': 'width: 99% !important; resize: vertical !important;'}),
Cubiczx
  • 1,005
  • 11
  • 10
0

this works for me: step 1: after displaying the form.as_p step 2: go to inspect the page where the form is displayed step 3: copy the id of that particular input field step 4: write the Internal CSS for that id

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 26 '22 at 02:09