Django Forms and Bootstrap - CSS and <divs> Classes
I am using Twitter Bootstrap with Django to render forms.
Bootstrap
can pretty well format your forms - provided you have the CSS
classes that it expects.
However, my problem is that the forms generated by Django {{ form.as_p }}
not render well with Bootstrap, since they do not 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 have <fieldset class="control-group">
, each <label>
has class="control-label"
, and each <input>
enclosed 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 each form field in Django is pretty painful:
Add class to Django label_tag () output
Is there a more reasonable way to use {{ form.as_p }}
, or iterate through fields, without having to manually specify something, or with a whole bunch of hacker attacks?
Hooray Victor
I like to use "django-crispy-forms" , which is the successor to django-uni-form. This is a great API and has great support for Bootstrap.
I use template filters to quickly migrate old code and quick forms and template tags when I need more control over the rendering.
Here 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>
If django-crispy-forms
cannot be used (for example, when the template processes each form field separately), jcmrgo's answer is the only way. Based on his answer, here is a solution for Bootstrap 3 (leaving its version for Boostrap 2) and with setting up field classes inside the template . Although field classes are not accessible from a template with the standard Django library (which leads to additional forms or template tags in other solutions), here is a solution that sets the correct classes to field tags without the need for 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 requires django-widget-tweaks
and widget_tweaks
added to INSTALLED_APPS
.
You can 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 %}
To add CSS attributes to Django generated forms, just use the following code in your forms.
Recepient = forms.ChoiceField(label=u'Recepient', widget=forms.Select(attrs={'id':'combobox'}))
It produces the following HTML code:
<label for="id_Recepient">Recepient</label> <select id="combobox" name="Recepient">
I definitely recommend https://github.com/dyve/django-bootstrap-toolkit
For example, you can create a class that defines attributes the way you want, and simply name 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)
Bootstrap style forms with <div>
, not <p>
s. So, if you want it to look beautiful, you need to go to the start path 100% IMHO. And here is my preferred way to do this:
Use the 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 »' reset='Reset Form' layout='horizontal' %}{% endbuttons %} </form>
Note the class attribute horizontal
in 1) 2) the layout of bootstrap_form and 3) the layout of the buttons.
Here is my version using django_tweaks with the best result. I find render_field easier to use than adding filters. I also added bootstrap-formatted warnings and turned off the navigator check (with the update). I'm relatively new to Django, so feel free to comment if you find any non-meaning
<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>
The easiest and easiest way is to define your own base class that extends the Django Form class and override its as_p
method for output in Bootstrap format. Then change your forms inherit from the new Form class instead of Django.
besides what other friends said, I would recommend using 'django-widget-tweaks'.
The answer will be something 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 %}