43

I have a django model like below

models.py

class Product(models.Model):
    name = models.CharField(max_length = 300)
    description = models.TextField(max_length = 2000)
    created = models.DateTimeField(auto_now_add = True)
    updated = models.DateTimeField(auto_now = True)

    def __unicode__(self):
        return self.name

forms.py

class ProductForm(ModelForm):
    class Meta:
        model = Product
        exclude = ('updated', 'created')

product_form.py(just an example)

 <form enctype="multipart/form-data" action="{% url 'add_a_product' %}" method="post">
         <div id="name">
           {{form.name}}
         </div> 
         <div id="description">
           {{form.description}}
         </div> 
   </form> 

Actually I want to display/render the html output like below

<input id="common_id_for_inputfields" type="text" placeholder="Name" class="input-calss_name" name="Name">

<input id="common_id_for_inputfields" type="text" placeholder="Description" class="input-calss_name" name="description">

So finally how to add attributes(id, placeholder, class)to the model form fields in the above code ?

Vini.g.fer
  • 11,639
  • 16
  • 61
  • 90
Shiva Krishna Bavandla
  • 25,548
  • 75
  • 193
  • 313
  • See documentation for [django.forms.Widget.attrs](https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#django.forms.Widget.attrs). This is implemented in the [attributes template](https://github.com/django/django/blob/stable/3.1.x/django/forms/templates/django/forms/widgets/attrs.html). – djvg Aug 11 '20 at 08:55
  • One thing is avoid using same ids in different inputs.. – Tariq Ahmed Nov 22 '21 at 05:55

9 Answers9

57

You can do the following:

#forms.py
class ProductForm(ModelForm):
    class Meta:
        model = Product
        exclude = ('updated', 'created')

    def __init__(self, *args, **kwargs):
        super(ProductForm, self).__init__(*args, **kwargs)
        self.fields['description'].widget = TextInput(attrs={
            'id': 'myCustomId',
            'class': 'myCustomClass',
            'name': 'myCustomName',
            'placeholder': 'myCustomPlaceholder'})
Denilson Sá Maia
  • 47,466
  • 33
  • 109
  • 111
Alex Parakhnevich
  • 5,172
  • 4
  • 26
  • 30
  • 12
    This worked well for me, except that it erased all options from my Select widget. I found out that you can skip the widget declaration all together and still set its attributes like so: `self.fields['description'].widget.attrs={ 'id': 'myCustomId', 'class': 'myCustomClass', 'name': 'myCustomName', 'placeholder': 'myCustomPlaceholder'}` Also seems a bit cleaner for me. – bjesus Oct 14 '16 at 12:11
  • 1
    If you want to add bootstrap classes to all forms without having to override constructor each time see my answer below. – hurlbz Jan 27 '17 at 18:14
  • Is there also a way to give the same id as the name? – cwhisperer Jan 23 '19 at 13:18
33

Field ids should be generated automatically by django, to override other fields:

class ProductForm(ModelForm):
    class Meta:
        model = Product
        exclude = ('updated', 'created')

    def __init__(self, *args, **kwargs):
        super(ProductForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs\
            .update({
                'placeholder': 'Name',
                'class': 'input-calss_name'
            })
mariodev
  • 13,928
  • 3
  • 49
  • 61
  • Are id's really SHOULD be generated automatically? You can override the id as well. – Alex Parakhnevich Oct 21 '13 at 08:59
  • You can override id as well (just like I showed you, you can override any attribute), but I think it's better to use django generated ids, unless you don't have any control over them (like when you use 3rd party js libraries or smth..) – mariodev Oct 21 '13 at 13:59
27

I really like Dmitriy Sintsov's answer but it doesn't work. Here's a version that does work:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    for field in iter(self.fields):
        self.fields[field].widget.attrs.update({
            'class': 'form-control'
    })

update

add this condition for better

if self.fields[field].widget.__class__.__name__ in ('AdminTextInputWidget' , 'Textarea' , 'NumberInput' , 'AdminURLFieldWidget', 'Select'): 
     self.fields[field].widget.attrs.update({ 'class': 'form-control' })
Saurabh Chandra Patel
  • 12,712
  • 6
  • 88
  • 78
Derick Hayes
  • 371
  • 3
  • 2
  • This is much better than having to form the entire Widget each time. I created a class BasicForm which extends forms.ModelForm that does this. I just extend BasicForm instead of model form and automatically get bootstrap classes on all forms. I went a step further and append the classes to any custom css classes which may already be there. See my answer for code example. – hurlbz Jan 27 '17 at 18:10
20

You can update forms.py as below

class ProductForm(ModelForm):
    class Meta:
        model = Product
        exclude = ('updated', 'created')
        widgets={
                   "name":forms.TextInput(attrs={'placeholder':'Name','name':'Name','id':'common_id_for_imputfields','class':'input-class_name'}),
                   "description":forms.TextInput(attrs={'placeholder':'description','name':'description','id':'common_id_for_imputfields','class':'input-class_name'}),
                }  
Nayan
  • 1,521
  • 2
  • 13
  • 27
kali sharma
  • 311
  • 2
  • 2
7

Slightly modified version of excellent mariodev answer, adding bootstrap class to all form fields, so I do not have to re-create form input widgets for each field manually (short Python 3.x super()):

class ProductForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].widget.attrs.update({
                'class': 'form-control'
            })
Dmitriy Sintsov
  • 3,821
  • 32
  • 20
3

Adding to answer from Derick Hayes I created a class BasicForm which extends forms.ModelForm that adds the bootstrap classes to every form that extends it.

For my forms I just extend BasicForm instead of model form and automatically get bootstrap classes on all forms. I went a step further and append the classes to any custom css classes which may already be there.

class BaseModelForm(forms.ModelForm):

def __init__(self, *args, **kwargs):
    super(BaseModelForm, self).__init__(*args, **kwargs)
    # add common css classes to all widgets
    for field in iter(self.fields):
        #get current classes from Meta
        classes = self.fields[field].widget.attrs.get("class")
        if classes is not None:
            classes += " form-control"
        else:
            classes = "form-control"
        self.fields[field].widget.attrs.update({
            'class': classes
        })
hurlbz
  • 383
  • 3
  • 16
3

add_class filter for adding a CSS class to form field:

 {% load widget_tweaks %}     
    <form enctype="multipart/form-data" action="{% url 'add_a_product' %}" method="post">
                 <div id="name">
                   {{form.name|add_class:"input-calss_name"}}
                 </div> 
                 <div id="description">
                   {{form.description|add_class:"input-calss_name"}}
                 </div> 
           </form> 

django-widget-tweaks library

  • If you don't want to have to override the default forms, this is the way to go! I've been using it on Django projects for years. `{% render_field field class+="form-check-input" %}` – Jon Jul 31 '19 at 20:07
2

You can do the following:

class ProductForm(ModelForm):
    name = forms.CharField(label='name ', 
            widget=forms.TextInput(attrs={'placeholder': 'name '}))
0

I know this is an old question but if someone is still looking to add custom class to all of his form fields, then you can use this one liner

class ProductForm(ModelForm):
    class Meta:
        model = Product
        exclude = ('updated', 'created')
def __init__(self, *args, **kwargs):
    super(ProductForm, self).__init__(*args, **kwargs)
    custom_attrs = {
        'class': 'form-control',
        'toggle-data': 'mydiv',
    }
    # adds our custom_attrs to each element of the form 
    [self.fields[i].widget.attrs.update(custom_attrs) for i in self.fields]
wetler
  • 374
  • 2
  • 11