210

Assume I have a form

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

Is there a way for me to define css classes on each field such that I can then use jQuery based on class in my rendered page?

I was hoping not to have to manually build the form.

Mikhail Korobov
  • 21,908
  • 8
  • 73
  • 65
ashchristopher
  • 25,143
  • 18
  • 48
  • 49
  • 9
    wow, everyone up vote this? http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs – Skylar Saveland Nov 03 '10 at 19:10
  • 12
    @skyl I would take it as an indication that it isn't easy to find in the django docs. I browsed and did several google searches too and couldn't find this, so I'm glad for the question and it gets my upvote. – Nils May 31 '12 at 17:54
  • I know it's an old thread but in Django form fields now have an id_fieldname class. – ratsimihah Aug 05 '13 at 16:13
  • 1
    See [this answer](http://stackoverflow.com/a/41129192/2369865) on how to define classes for a form field inside a template while respecting existing field classes – dimyG Dec 13 '16 at 20:47

16 Answers16

193

Yet another solution that doesn't require changes in python code and so is better for designers and one-off presentational changes: django-widget-tweaks. Hope somebody will find it useful.

Mikhail Korobov
  • 21,908
  • 8
  • 73
  • 65
  • 63
    The **only** sane solution, I must say. Thank you!. Python code, and especially in form's definition, is the last place to put stuff for styling - these definitely belong to the templates. – Boris Chervenkov Nov 12 '11 at 23:09
  • 5
    This is a great library! It's a shame this answer is buried at the bottom. – Chris Lacasse Jan 11 '12 at 15:35
  • 1
    Excellent! I had developed some filters to do these stuff, but this project is much more powerful. Thanks! – msbrogli Jun 08 '12 at 17:19
  • This should be the accepted answer -- much better approach. Too bad this adds another dependency though. – Erik Aug 27 '12 at 02:10
  • it seem that this does not work for Jinjia2 templates. I wish it did because it is a great solution. {{ myform.email|safe|add_class("css_class_1 css_class_2") }} – David Dehghan Feb 28 '13 at 09:22
  • Yes, it is a template tag/filter library for django template engine; jinja2 support would require some work (pull requests are welcome). – Mikhail Korobov Feb 28 '13 at 09:44
  • 1
    actually it works for Jinja2 also :-) I changed the order of safe fileter and added parenthesis instead of colon {{ myform.email|add_class("css_class_1 css_class_2")|safe }} thanks for writing this. it should be part of Django. – David Dehghan Feb 28 '13 at 10:07
  • wow :) I guess you're using some jinja2 integration package for django? – Mikhail Korobov Feb 28 '13 at 10:19
  • 1
    Even to a developer, it does feel like the templates are the right place for this to live. – HorseloverFat Apr 29 '14 at 13:13
  • `django-widget-tweaks` does not seem to be supported. Are there any alternatives? – utapyngo Dec 28 '17 at 16:58
  • 2
    django-widget-tweaks is a jazzband project for a year or so, anyone can help - join jazzband org, and you have commit rights to django-widget-tweaks. jazzband org is allowed to make pypi releases. So if someone wants to help, he/she doesn't even need me. Let's see how it would work :) It looks like people are using the package and think it's good, but nobody is taking a step to maintain it. It needs maintainers. Package is very simple: less than 200 lines of code, of which only about 30 lines is the core logic. 90%+ test coverage. If you find it useful please help. – Mikhail Korobov Dec 28 '17 at 18:57
97

Answered my own question. Sigh

http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs

I didn't realize it was passed into the widget constructor.

ashchristopher
  • 25,143
  • 18
  • 48
  • 49
  • 3
    does that imply it cannot be used with ModelForm class? – xster May 03 '11 at 02:32
  • 2
    No, you would just need to explicitly define the form field in the model form so you could define the widget. Or use the solution listed below to avoid having to muck with the form field. – Tom May 12 '11 at 16:05
84

Here is another solution for adding class definitions to the widgets after declaring the fields in the class.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
  • 25,143
  • 18
  • 48
  • 49
  • 4
    For ModelForms, this is often better as you don't need to be aware of the default form field being used, and you can dynamically set different classes based on runtime conditions. Cleaner than meta coding hacks... – Daniel Naab Dec 30 '08 at 22:37
  • 1
    That assumes that you want an input for every field in a form, which is often not the case. A form is not necessarily tied to a model - it might be tied to many models. In the case that the model fields and the form fields have a 1:1 relationship, then yes, ModelForm is a much better choice. – ashchristopher Dec 31 '08 at 04:29
49

Use django-widget-tweaks, it is easy to use and works pretty well.

Otherwise this can be done using a custom template filter.

Considering you render your form this way :

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
</form>

form.subject is an instance of BoundField which has the as_widget method.

you can create a custom filter "addcss" in "my_app/templatetags/myfilters.py"

from django import template

register = template.Library()

@register.filter(name='addcss')
def addcss(value, arg):
    css_classes = value.field.widget.attrs.get('class', '').split(' ')
    if css_classes and arg not in css_classes:
        css_classes = '%s %s' % (css_classes, arg)
    return value.as_widget(attrs={'class': css_classes})

And then apply your filter:

{% load myfilters %}
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject|addcss:'MyClass' }}
    </div>
</form>

form.subjects will then be rendered with the "MyClass" css class.

Hope this help.

EDIT 1

  • Update filter according to dimyG's answer

  • Add django-widget-tweak link

EDIT 2

  • Update filter according to Bhyd's comment
Charlesthk
  • 9,394
  • 5
  • 43
  • 45
  • 3
    This is great! It's DRY and it separates display layer from control layer. +1 from me! – alekwisnia Nov 20 '13 at 15:12
  • 1
    However, now I've noticed one drawback. If I define class in widget attrs, then they are overriden by this 'addcss' filter. Do you have any ideas how to merge that? – alekwisnia Nov 21 '13 at 14:16
  • 1
    I mean, as_widget() overrides attrs. How to ensure it uses existing attrs and extends them with new one? – alekwisnia Nov 22 '13 at 16:08
35

If you want all the fields in the form to inherit a certain class, you just define a parent class, that inherits from forms.ModelForm, and then inherit from it

class BaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'someClass'


class WhateverForm(BaseForm):
    class Meta:
        model = SomeModel

This helped me to add the 'form-control' class to all of the fields on all of the forms of my application automatically, without adding replication of code.

Stan
  • 8,710
  • 2
  • 29
  • 31
Oleg Belousov
  • 9,981
  • 14
  • 72
  • 127
34

Expanding on the method pointed to at docs.djangoproject.com:

class MyForm(forms.Form): 
    comment = forms.CharField(
            widget=forms.TextInput(attrs={'size':'40'}))

I thought it was troublesome to have to know the native widget type for every field, and thought it funny to override the default just to put a class name on a form field. This seems to work for me:

class MyForm(forms.Form): 
    #This instantiates the field w/ the default widget
    comment = forms.CharField()

    #We only override the part we care about
    comment.widget.attrs['size'] = '40'

This seems a little cleaner to me.

Dave
  • 1,196
  • 15
  • 22
  • This is precisely what others have said, ages ago, except that you're doing it with 'size' rather than 'class' which was requested. – Chris Morgan Nov 25 '10 at 00:11
  • 3
    @Chris Morgan Actually, he's the first to suggest using "comment.widget.attrs" in the Form declaration itself (instead of messing with __init__ or doing it in the view). – DarwinSurvivor Jul 26 '12 at 23:14
  • This also works more reliably: `forms.FloatField` for example are finicky when trying to add attrs during instantiation. And by "finicky" I mean "I couldn't get it work at all". But this method worked for `FloatField`. – BLimitless Jul 28 '23 at 05:39
24

Simply add the classes to your form as follows.

class UserLoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={
        'class':'form-control',
        'placeholder':'Username'
        }
    ))
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
        'class':'form-control',
        'placeholder':'Password'
        }
    ))
paulc
  • 455
  • 4
  • 10
17

Here is Simple way to alter in view. add below in view just before passing it into template.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Spiderman
  • 1,969
  • 1
  • 14
  • 14
6

You can try this..

class SampleClass(forms.Form):
  name = forms.CharField(max_length=30)
  name.widget.attrs.update({'class': 'your-class'})
...

You can see more information in: Django Widgets

Andres Quiroga
  • 101
  • 1
  • 2
5

Here is a variation on the above which will give all fields the same class (e.g. jquery nice rounded corners).

  # Simple way to assign css class to every field
  def __init__(self, *args, **kwargs):
    super(TranslatedPageForm, self).__init__(*args, **kwargs)
    for myField in self.fields:
      self.fields[myField].widget.attrs['class'] = 'ui-state-default ui-corner-all'
timlinux
  • 51
  • 1
  • 1
2

In case that you want to add a class to a form's field in a template (not in view.py or form.py) for example in cases that you want to modify 3rd party apps without overriding their views, then a template filter as described in Charlesthk answer is very convenient. But in this answer the template filter overrides any existing classes that the field might has.

I tried to add this as an edit but it was suggested to be written as a new answer.

So, here is a template tag that respects the existing classes of the field:

from django import template

register = template.Library()


@register.filter(name='addclass')
def addclass(field, given_class):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existing_classes.find(given_class) == -1:
            # if the given class doesn't exist in the existing classes
            classes = existing_classes + ' ' + given_class
        else:
            classes = existing_classes
    else:
        classes = given_class
    return field.as_widget(attrs={"class": classes})
Community
  • 1
  • 1
dimyG
  • 687
  • 9
  • 19
1

As it turns out you can do this in form constructor (init function) or after form class was initiated. This is sometimes required if you are not writing your own form and that form is coming from somewhere else -

def some_view(request):
    add_css_to_fields = ['list','of','fields']
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    else:
        form = SomeForm()

    for key in form.fields.keys():
        if key in add_css_to_fields:
            field = form.fields[key]
            css_addition = 'css_addition '
            css = field.widget.attrs.get('class', '')
            field.widget.attrs['class'] = css_addition + css_classes

    return render(request, 'template_name.html', {'form': form})
Niki.py
  • 53
  • 4
1

You could also use Django Crispy Forms, it's a great tool to define forms in case you'd like to use some CSS framework like Bootstrap or Foundation. And it's easy to specify classes for your form fields there.

Your form class would like this then:

from django import forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Submit, Field
from crispy_forms.bootstrap import FormActions

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

    helper = FormHelper()
    helper.form_class = 'your-form-class'
    helper.layout = Layout(
        Field('name', css_class='name-class'),
        Field('age', css_class='age-class'),
        Field('django_hacker', css-class='hacker-class'),
        FormActions(
            Submit('save_changes', 'Save changes'),
        )
    )
Dmitrii Mikhailov
  • 5,053
  • 7
  • 43
  • 69
1

You can get styling options for all kinds of input fields here

A widget is Django’s representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget.

email = forms.EmailField(label='Your email', widget=forms.EmailInput(attrs={'class': 'ui segment teal'}))
MD SHAYON
  • 7,001
  • 45
  • 38
0

If you are using ModelForm and has included the necessary fields with fields property, there is a way to define css classes for them. This worked better than the 'for loop' method for me because I wanted different types of css classes for different input fields.

fields = ( 'date', 'title'),
widgets = {'date': forms.DateInput(attrs={'class': 'datepicker'}),
          'title': forms.TextInput(attrs={'class': 'title'})}

Or you can also try setting them via the constructor of the Form class

def __init__(self, *args, **kwargs):
       super().__init__(*args, **kwargs)
       self.fields['date'].widget.attrs.update({'class': 'datepicker'})
       self.fields['title'].widget.attrs.update({'class':'title'})
Sebastian Gomes
  • 775
  • 9
  • 12
0

To define css class in django, simply you can use widgets in forms.

Example:

class ProcessForm(forms.ModelForm):  
    class Meta:  
        model = Processmachine 
        fields = ['machine_name', 'operation_no', 'process_uploadfile'] #https://docs.djangoproject.com/en/3.0/ref/forms/widgets/
        widgets = { 'machine_name': forms.TextInput(attrs={ 'class': 'form-control' }), 
            'operation_no': forms.TextInput(attrs={ 'class': 'form-control' }),
            'process_uploadfile': forms.ClearableFileInput(attrs={ 'class': 'form-control' }),
      }

In above form, I have used css class inside attrs

Manoj Tolagekar
  • 1,816
  • 2
  • 5
  • 22