215

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.

4
  • 9
    wow, everyone up vote this? docs.djangoproject.com/en/dev/ref/forms/widgets/… Commented Nov 3, 2010 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. Commented May 31, 2012 at 17:54
  • I know it's an old thread but in Django form fields now have an id_fieldname class. Commented Aug 5, 2013 at 16:13
  • 1
    See this answer on how to define classes for a form field inside a template while respecting existing field classes Commented Dec 13, 2016 at 20:47

17 Answers 17

196

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.

Sign up to request clarification or add additional context in comments.

11 Comments

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.
This is a great library! It's a shame this answer is buried at the bottom.
Excellent! I had developed some filters to do these stuff, but this project is much more powerful. Thanks!
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.
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.
|
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.

2 Comments

does that imply it cannot be used with ModelForm class?
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.
87

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'

2 Comments

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...
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.
51

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

3 Comments

This is great! It's DRY and it separates display layer from control layer. +1 from me!
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?
I mean, as_widget() overrides attrs. How to ensure it uses existing attrs and extends them with new one?
36

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.

1 Comment

please start class names with uppercase (also, BaseForm seems a lot better name for it)
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.

3 Comments

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 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).
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.
25

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'
        }
    ))

Comments

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'}

Comments

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

Comments

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'

Comments

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})

2 Comments

Thanks for your suggestion. I edited my answer accordingly with a more "pythonic" approach.
great!!, that is what i was looking for
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})

Comments

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'),
        )
    )

Comments

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'}))

Comments

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'})

Comments

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

Comments

0

Disclaimer: I would still recommend using django-widget-tweaks in most cases. Nonetheless, I want to include the below answer separately, because it can still be useful and isn't documented anywhere else that I can find.


I'm not sure about earlier versions, but in Django 5.0 there is a slightly easier way of handling this.

If you're using a ModelForm, then you can modify the BoundField objects to specify a css_classes attribute. I presume the same is true for standard (non-model) form classes, but I have not confirmed it.

class ModelForm(forms.ModelForm):
    # (optional - see notes below) 
    # template_name = "myapp/forms/div.html"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self["name"].css_classes = "special"        

For more general approach, you can iterate over the form class's form field objects to perform some logic to dynamically assign a class based on field type, widget, or whatever suits your needs.

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            # where field_name is "name"
            # where field is <django.forms.fields.CharField object...>
            self[field_name] = ...

An important note here is the difference between the form field objects (like forms.CharField) and the BoundField objects. The former is the field definition for the form class, but the latter is what's used to render the template.

self.name    # <-- form field object (does not have css_classes attribute)
self["name"] # <-- BoundField object (has css_classes attribute)

Optionally, you can specify the template_name class attribute for your form class to point to a relative path from your app's templates/ directory to use instead of Django's standard template.

Note: There are other form class attributes (like form_template_name or template_name_div, among others) which can probably be used to to control this more granularly. This is just what I found to work for me.


References:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.