70

I'm using Twitter Bootstrap with Django to render forms.

Bootstrap can format your forms quite nicely - as long as you have the CSS classes it expects included.

However, my issue is that the forms generated by Django's {{ form.as_p }} don't render well with Bootstrap, as they don't have these classes.

For example, the output from Django:

    <form class="horizontal-form" action="/contact/" method="post">
        <div style='display:none'>
            <input type='hidden' name='csrfmiddlewaretoken' 
                   value='26c39ab41e38cf6061367750ea8c2ea8'/>
        </div>
        <p><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="FOOBAR" maxlength="20" /></p>
        <p><label for="id_directory">Directory:</label> <input id="id_directory" type="text" name="directory" value="FOOBAR" maxlength="60" /></p>
       <p><label for="id_comment">Comment:</label> <textarea id="id_comment" rows="10" cols="40" name="comment">Lorem ipsum dolor sic amet.</textarea></p>
       <p>
           <label for="id_server">Server:</label>
           <select name="server" id="id_server">
               <option value="">---------</option>
               <option value="1" 
                   selected="selected">sydeqexcd01.au.db.com</option>
               <option value="2">server1</option>
               <option value="3">server2</option>
               <option value="4">server3</option>
           </select>
       </p>
       <input type="submit" value="Submit" />
    </form>

From what I can tell, Bootstrap requires that your forms has a <fieldset class="control-group">, each <label> has class="control-label", and each <input> is wrapped in a <div>:

<fieldset class="control-group">
    <label class="control-label" for="input01">Text input</label>
    <div class="controls">
        <input type="text" class="xlarge" name="input01">
        <p class="help-text">Help text here. Be sure to fill this out like so, or else!</p>
    </div>
</fieldset>

However, adding custom CSS labels to every form field in Django is rather painful:

Add class to Django label_tag() output

Is there a smarter way of either using {{ form.as_p }}, or iterating through the fields, without having to manually specify things, or do a whole bunch of hackery?

Cheers, Victor

Jignasha Royala
  • 1,087
  • 10
  • 23
victorhooi
  • 14,225
  • 20
  • 79
  • 112
  • 2
    See also http://stackoverflow.com/questions/18158293/how-can-i-work-around-the-need-for-bootstrap-3s-form-control-class – Bryce Aug 07 '14 at 05:23
  • 2
    You could check out [django-bootstrap-form](https://github.com/tzangms/django-bootstrap-form) and/or [django-bootstrap](https://github.com/earle/django-bootstrap) Do let us know which one you end up using and why! – Gumbah Dec 12 '11 at 20:34
  • 1
    django-bootstrap-form appears to be very light on functionality at the moment. django-bootstrap is very promising, though, and is using the correct approach for django as far as I can tell. It would definitely be worth contributing to. – bogeymin Jan 14 '12 at 01:19

11 Answers11

56

This is what I came up with:

<form class="form-horizontal" method="post">{% csrf_token %}
    <fieldset>
        <legend>{{ title }}</legend>
        {% for field in form %}
            {% if field.errors %}
                <div class="control-group error">
                    <label class="control-label">{{ field.label }}</label> 
                    <div class="controls">{{ field }}
                        <span class="help-inline">
                            {% for error in  field.errors %}{{ error }}{% endfor %}
                        </span>
                    </div>
                </div>
            {% else %}
                <div class="control-group">
                    <label class="control-label">{{ field.label }}</label> 
                    <div class="controls">{{ field }}
                        {% if field.help_text %}
                            <p class="help-inline"><small>{{ field.help_text }}</small></p>
                        {% endif %}
                    </div>
                </div>
            {% endif %}
        {% endfor %}
    </fieldset>
    <div class="form-actions">
        <button type="submit" class="btn btn-primary" >Submit</button>
    </div>
</form>
Wai Ha Lee
  • 7,664
  • 52
  • 54
  • 80
jcmrgo
  • 771
  • 6
  • 4
  • 2
    That is pretty awesome! Thanks. I was hoping to also end up with an ability to get the label to be a span that is connected to the side of a textbox. I would guess that I need to install some other package to manage that. – Karl Henselin Oct 22 '14 at 02:46
  • 1
    And if you need to use this across multiple templates, use a [reusable form template](https://docs.djangoproject.com/en/dev/topics/forms/#reusable-form-templates). – phoenix Jul 26 '16 at 01:12
  • Awesome, very useful! – mfnx Aug 22 '18 at 10:46
49

I like to use "django-crispy-forms" which is the successor to django-uni-form. It's a great little API and has great support for Bootstrap.

I tend to use the template filters for quickly porting old code and quick forms, and the template tags when I need more control over the rendering.

erikcw
  • 9,517
  • 13
  • 54
  • 72
43

When django-crispy-forms cannot be used (e.g. when the template treats individually each field of the form), jcmrgo's answer is the only way to go. Based on his answer, here is a solution for Bootstrap 3 (leaving his version for Boostrap 2), and with adjustment of field classes inside the template. While field classes are not reachable from within the template with Django's standard library (which leads to that extra forms or template tags in other solutions), here is a solution that sets the right classes to field tags without having to code outside the template:

{% load i18n widget_tweaks %}

<form class="form-horizontal" role="form" action="." method="post">
    {% csrf_token %}
    {% for field in form %}
        {% if field.errors %}
            <div class="form-group has-error">
                <label class="col-sm-2 control-label" for="id_{{ field.name }}">{{ field.label }}</label>
                <div class="col-sm-10">
                    {{ field|attr:"class:form-control" }}
                    <span class="help-block">
                        {% for error in  field.errors %}{{ error }}{% endfor %}
                    </span>
                </div>
            </div>
        {% else %}
            <div class="form-group">
                <label class="col-sm-2 control-label" for="id_{{ field.name }}">{{ field.label }}</label>
                <div class="col-sm-10">
                    {{ field|attr:"class:form-control" }}
                    {% if field.help_text %}
                        <p class="help-block"><small>{{ field.help_text }}</small></p>
                    {% endif %}
                </div>
            </div>
        {% endif %}
    {% endfor %}
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
        </div>
    </div>
</form>

This needs django-widget-tweaks installed and widget_tweaks added to INSTALLED_APPS.

phoenix
  • 3,988
  • 1
  • 29
  • 33
Iodnas
  • 3,154
  • 20
  • 25
  • 5
    As of 2015, this is the BEST answer. Especially if you're in hurry (need to deliver) and not-in-the-mood/don't-have-time to learn *yet another* library like crispy forms. Thanks a LOT sir! – Asotos Apr 12 '15 at 18:40
  • 2
    As of 2016, this is still the best answer for easily integrating without having to learn crisp. – James Mertz Apr 04 '16 at 17:42
  • This really should be the default answer. django-crispy-forms is nice if you don't want to template your forms and have time for a large library which takes time to learn. However in the context of the original question and in the name of time to implement; this is the best answer. – Routhinator Apr 24 '16 at 19:32
  • To set the placeholder - if there is one defined in the form - you can do additionally `placeholder="{{ field.field.widget.attrs.placeholder }}`. Django also can provide the needed `id` by itself: `id="{{ field.id_for_label }}"` and the name by `name="{{ field.html_name }}` – rwx May 09 '16 at 16:51
  • @rwx the field in the code above is being generated dynamically. Where will one add the `placeholder="{{ field.field.widget.attrs.placeholder }}` – Anupam Feb 08 '17 at 10:20
  • @AnupamJain e.g. if you have an input you can do to set the placeholder to the string defined in the django form – rwx Feb 08 '17 at 16:34
  • @rwx Right, but you cant do that in the above code since there is no `` tag since the field is being generated dynamically (and the code uses template filters). I think we need to use render_field instead to render the field to be able to use a dynamic placeholder. I struggled with that yesterday and documented the finding here: http://stackoverflow.com/questions/42111220/use-field-label-as-placeholder-with-django-widget-tweaks) – Anupam Feb 09 '17 at 08:21
  • 2
    As of 2018, this is still the best answer. I like that it keeps presentation stuff in the template, as opposed to crispy forms, which forces me to put it in python code. And, I like that it is fairly straightforward to get up and running. My only suggestion is to use "render field" instead of a filter, which is described in more detail in another answer below. – Deven Jan 22 '18 at 03:27
  • As of 2020, I still use this a lot because I like how easily I can tweak every individual form field. – RusI May 12 '20 at 18:30
11

You could do something like this:

{% for field in form %}
<fieldset class="control-group">
    <label class="control-label" for="id_{{ field.name }}">{{ field.label }}</label>
    <div class="controls">
        {{ field }}
        <p class="help-text">{{ field.help_text }} </p>
    </div>
</fieldset>
{% endfor %}
c4urself
  • 3,831
  • 18
  • 27
8

In order to add CSS attributes to Django generated forms it's enough to use the following code in your forms.py:

Recepient = forms.ChoiceField(label=u'Recepient', widget=forms.Select(attrs={'id':'combobox'}))

It will produce the following HTML code:

<label for="id_Recepient">Recepient</label>
<select id="combobox" name="Recepient">
Jaro
  • 1,142
  • 11
  • 10
4

I definitely recommend https://github.com/dyve/django-bootstrap-toolkit

bogtan
  • 744
  • 2
  • 12
  • 22
4

Here is my version using django_tweaks with a better looking result. I find render_field lighter to use than adding filters. I have also added bootstrap formatted alert messages and turned off navigator validation (with novalidate). I am relatively new to Django so don't hesitate to comment if you find any non-sense

<form class="large" method="post" action="/suscript/" novalidate>
    {% csrf_token %}
    <fieldset>
        <legend>{{ title }}</legend>
        {% for field in form %}
            <div class="control-group {%if field.errors %}error{%endif%}">
                <div class="input-group controls">
                    <label class="input-group-addon control-label" id="{{field.label|safe}}">{{ field.label }}</label>
                    {% render_field field type="text" class="form-control" placeholder="" aria-describedby="field.label|safe" %}
                </div>
                    {% for error in field.errors %}
                         <div class="alert alert-danger">
                            <strong>{{ error|escape }}</strong>
                         </div>
                    {% endfor %}

                    {% if field.help_text %}
                        <p class="help-inline">{{ field.help_text|safe }}</p>
                    {% endif %}
            </div>

        {% endfor %}
    </fieldset>
    <div class="form-actions">
        <button type="submit" class="btn btn-primary" >Submit</button>
    </div>
</form>
3

For Example, you could created a class which defines the attributes the way you want and just call it accordingly.

class ContactForm(ModelForm):
    class Meta:
      model = Contact
      created = MyDatePicker()

class Uniform(forms):
  def __init__(self, *args, **kwargs):
      attrs = kwargs.pop("attrs",{})
      attrs["class"] = "span3"
      kwargs["attrs"] = attrs
      super(Uniform, self).__init__(*args, **kwargs)

class MyDatePicker(Uniform,forms.DateInput)
  def __init__(self, *args, **kwargs):
      attrs = kwargs.pop("attrs",{})
      attrs["class"] = "datepick" 
      attrs["id"] =kwargs.get('datetag', '')
      kwargs["attrs"] = attrs
      super(MyDatePicker, self).__init__(*args, **kwargs)
lakesh
  • 25,623
  • 57
  • 163
  • 254
3

Bootstrap styles forms with <div>s rather than <p>s. So, if you want it to look nice, you need to go bootstrap way 100% IMHO. And here is my preferred way of doing it:

Use django-bootstrap3 app. Example:

{% load bootstrap3 %}

<form class="signup form-horizontal" id="signup_form" method="post" action="{% url 'account_signup' %}">
    {% csrf_token %}
    {% bootstrap_form form layout="horizontal" %}
    {% buttons submit='Sign Up &raquo;' reset='Reset Form' layout='horizontal' %}{% endbuttons %}
</form>

Notice the horizontal in 1) form class attribute 2) bootstrap_form layout and 3) buttons layout.

mehmet
  • 6,023
  • 3
  • 31
  • 42
  • 1
    I think this is the most elegant approach, maybe it is good to add that you can also render individual fields like so: `{% bootstrap_field form.field_name %}` and have the ability to add formsets etc. – BartDur Nov 11 '16 at 12:24
2

The fastest and easiest way would be to define your own base class that extends the Django Form class, and redefine its as_p method to output in the format Bootstrap requires. Then change your forms to inherit from your new Form class instead of Django's.

-1

besides what other friends said, I would recommend using 'django-widget-tweaks'.

the answer would be sth like this:

{% for field in form %}
      <label for="{{ field.label }}">{{ field.label }}</label>
      {{ field|add_class:"form-control" }}
      <span class="error-block">{{ field.errors }}</span>
{% endfor %}
Lord ST
  • 473
  • 7
  • 13