How to add class, id, placeholder attributes to a field in django model forms
Asked Answered
H

9

44

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 ?

Hallmark answered 21/10, 2013 at 8:39 Comment(2)
See documentation for django.forms.Widget.attrs. This is implemented in the attributes template.Cheng
One thing is avoid using same ids in different inputs..Escobar
E
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'})
Estafette answered 21/10, 2013 at 8:52 Comment(3)
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.Paraplegia
If you want to add bootstrap classes to all forms without having to override constructor each time see my answer below.Jauregui
Is there also a way to give the same id as the name?Reactive
D
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'
            })
Disinfect answered 21/10, 2013 at 8:54 Comment(2)
Are id's really SHOULD be generated automatically? You can override the id as well.Estafette
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..)Disinfect
L
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' })
Leilaleilah answered 15/7, 2015 at 3:9 Comment(1)
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.Jauregui
D
22

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'}),
                }  
Dateless answered 17/1, 2014 at 4:28 Comment(0)
W
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'
            })
Waal answered 23/6, 2015 at 11:35 Comment(1)
It also works if you use "self.fields" insted of "self.Meta.fields".Branny
J
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
        })
Jauregui answered 27/1, 2017 at 18:13 Comment(0)
T
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

Trisa answered 27/10, 2017 at 12:5 Comment(1)
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" %}Demonetize
O
2

You can do the following:

class ProductForm(ModelForm):
    name = forms.CharField(label='name ', 
            widget=forms.TextInput(attrs={'placeholder': 'name '}))
Overline answered 3/6, 2015 at 20:49 Comment(0)
G
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]
Grubb answered 10/10, 2020 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.