Django Crispy Forms
Django Crispy Forms
Release 1.0.0
Miguel Araujo
Contents
User Guide
1.1 Installation . . . . . . . . . . .
1.2 crispy filter . . . . . . . . . . .
1.3 {% crispy %} tag with forms . .
1.4 FormHelper . . . . . . . . . . .
1.5 Layouts . . . . . . . . . . . . .
1.6 {% crispy %} tag with formsets
1.7 Updating layouts on the go . . .
1.8 Frequently Asked Questions . .
.
.
.
.
.
.
.
.
3
3
5
6
12
15
22
24
30
API documentation
2.1 API helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 API Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3 API templatetags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
33
34
37
Developer Guide
3.1 Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
41
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
45
ii
django-crispy-forms provides you with a |crispy filter and {% crispy %} tag that will let you control the rendering behavior of your Django forms in a very elegant and DRY way. Have full control without writing custom form
templates. All this without breaking the standard way of doing things in Django, so it plays nice with any other form
application.
Contents
Contents
CHAPTER 1
User Guide
1.1 Installation
1.1.1 Installing django-crispy-forms
Install latest stable version into your python path using pip or easy_install:
pip install --upgrade django-crispy-forms
Or, if youd like to install the development version as a git repository (so you can git pull updates, use the -e flag
with pip install, like so:
pip install -e git+git://github.com/maraujop/django-crispy-forms.git@dev#egg=django-crispy-forms
In production environments, always activate Django template cache loader. This is available since Django 1.2 and what
it does is basically load templates once, then cache the result for every subsequent render. This leads to a significant
performance improvement. See how to set it up using fabulous Django docs page.
foundation Foundation In creators words The most advanced responsive front-end framework in the world.
This template pack is externally available through crispy-forms-foundation
If your form CSS framework is not supported and its open source, you can create a template pack for it and submit a
pull request in Github or create a crispy-forms-templatePackName project and let me know, so I can link it.
You can set your default template pack for your project using CRISPY_TEMPLATE_PACK Django settings variable,
setting it to one of the previous keywords:
CRISPY_TEMPLATE_PACK = uni_form
In Linux You can use rpl to easily find and update the proper lines. Run in the root of your project the following
command. It is strongly recommended that you have your project in a VCS or a backup, so you can rollback if
something goes wrong:
rpl -R uni_form. crispy_forms. .
Using rpl:
rpl -R "{% load uni_form_tags %}" "{% load crispy_forms_tags %}" .
4. Until version 1.2.0 former tags and filters names worked without changing them, current versions will force
updating your filters and tags:
|as_uni_form ----->
{% uni_form %} --->
|as_uni_errors --->
|as_uni_field ---->
|crispy
{% crispy %}
|as_crispy_errors
|as_crispy_field
Using rpl:
rpl -R "|as_uni_form" "|crispy" .
rpl -R "{% uni_form" "{% crispy" .
There is one filter that has been turned into a tag for extra layout power, so former filter name will not work. You will
only need to update this if you have custom or overriden templates in your project:
field|with_class ------> {% crispy_field field %}
6. crispy-forms renders your layouts strictly, exactly the fields mentioned, if you want crispy-forms to work the
same way as django-uni-form you can set new FormHelper attribute render_unmentioned_fields to
True.
<!-- note that theres also blue.uni-form.css and dark.uni-form.css available if you want to try chan
<link rel="stylesheet" href="{{ STATIC_URL }}uni_form/uni-form.css" type="text/css" />
<link rel="stylesheet" href="{{ STATIC_URL }}uni_form/default.uni-form.css" type="text/css" />
<!-- uni-form JS library, optional -->
<script src="{{ STATIC_URL }}uni_form/uni-form.jquery.js" type="text/javascript"></script>
3. If you are using uni_form template pack, dont forget to add the class uniForm to your form.
4. Refresh and enjoy!
1.3.1 Fundamentals
For the rest of this document we will use the following example form for showing how to use a helper. This form is in
charge of gathering some user information:
class ExampleForm(forms.Form):
like_website = forms.TypedChoiceField(
label = "Do you like this website?",
choices = ((1, "Yes"), (0, "No")),
coerce = lambda x: bool(int(x)),
widget = forms.RadioSelect,
initial = 1,
required = True,
)
favorite_food = forms.CharField(
label = "What is your favorite food?",
max_length = 80,
required = True,
)
favorite_color = forms.CharField(
label = "What is your favorite color?",
max_length = 80,
required = True,
)
favorite_number = forms.IntegerField(
label = "Favorite number",
required = False,
)
notes = forms.CharField(
label = "Additional notes or feedback",
required = False,
)
Lets see how helpers works step by step, with some examples explained. First you will need to import FormHelper:
Your helper can be a class level variable or an instance level variable, if you dont know what this means you might
want to read the article Be careful how you use static variables in forms. As a rule of thumb, if you are not going
to manipulate a FormHelper in your code, like in a view, you should be using a static helper, otherwise you should be
using an instance level helper. If you still dont understand the subtle differences between both, use an instance level
helper, because you wont end up suffering side effects. As in the next steps I will show you how to manipulate the
form helper, so we will create an instance level helper. This is how you would do it:
from crispy_forms.helper import FormHelper
class ExampleForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
As you can see you need to call the base class constructor using super and override the constructor. This helper
doesnt set any form attributes, so its useless. Lets see how to set up some basic FormHelper attributes:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class ExampleForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = id-exampleForm
self.helper.form_class = blueForms
self.helper.form_method = post
self.helper.form_action = submit_survey
self.helper.add_input(Submit(submit, Submit))
Note that we are importing a class called Submit that is a layout object. We will see what layout objects are in detail
later on, for now on lets just say that this adds a submit button to our form, so people can send their survey.
Weve also done some helper magic. FormHelper has a list of attributes that can be set, that effect mainly form
attributes. Our form will have as DOM id id-exampleForm, it will have as DOM CSS class blueForms, it will
use http POST to send information and its action will be set to reverse(submit_survey).
Lets see how to render the form in a template.
example_form, we would render it doing:
{% load crispy_forms_tags %}
{% crispy example_form example_form.helper %}
Notice that the {% crispy %} tags expects two parameters: first the form variable and then the helper. In this case
we use the FormHelper attached to the form, but you could also create a FormHelper instance and pass it as a
context variable. Most of the time, you will want to use the helper attached. Note that if you name your FormHelper
attribute helper you will only need to do:
{% crispy form %}
What youll get is the form rendered as HTML with awesome bits. Specifically...
Opening and closing form tags, with id, class, action and method set as in the helper:
<form action="/submit/survey/" class="uniForm blueForms" method="post" id="id-exampleForm">
[...]
</form>
Submit button:
<div class="buttonHolder">
<input type="submit" name="submit" value="Submit" class="submit submitButton" id="submit-id</div>
@login_required()
def inbox(request, template_name):
example_form = ExampleForm()
redirect_url = request.GET.get(next)
# Form handling logic
[...]
if redirect_url is not None:
example_form.helper.form_action = reverse(submit_survey) + ?next= + redirectUrl
We are changing form_action helper property in case the view was called with a next GET parameter.
Then you will have to write a little of html code surrounding the forms:
<form action="{% url submit_survey %}" class="uniForm" method="post">
{% crispy first_form %}
{% crispy second_form %}
</form>
You can read a list of Helper attributes you can set and what they are for.
For example this setting would generate <input class"textinput inputtext" .... The key of the dictionary textinput is the Djangos default class, the value is what you want it to be substituted with, in this case we
are keeping textinput.
Im using a jsonview decorator from django-jsonview. In our client side using jQuery would look like:
var example_form = #example-form;
$.ajax({
url: "{% url save_example_form %}",
type: "POST",
data: $(example_form).serialize(),
success: function(data) {
if (!(data[success])) {
// Here we replace the form, for the
$(example_form).replaceWith(data[form_html]);
}
else {
10
// Here you can show the user a success message or do whatever you need
$(example_form).find(.success-message).show();
}
},
error: function () {
$(example_form).find(.error-message).show()
}
});
Obviously, you can adjust this snippets to your needs, or class based views or favorite frontend library.
Warning: When replacing form html, you need to bind events using live or on jQuery method.
The way you do horizontal forms in Bootstrap version 3 is setting some col-lg-X classes in labels and divs wrapping
fields. This would mean a lot of hassle updating your layout objects for settings these classes, too much verbosity.
Instead some FormHelper attributes have been added to help you easily achieve this. You will need to set only three
attributes:
helper.form_class = form-horizontal
helper.label_class = col-lg-2
helper.field_class = col-lg-8
helper.layout = Layout(
email,
password,
remember_me,
StrictButton(Sign in, css_class=btn-default),
)
Of course you can set your widths as you like, it doesnt have to be exactly like this.
11
email,
password,
remember_me,
StrictButton(Sign in, css_class=btn-default),
)
If you need to set attributes in a field, you have to use InlineField instead of Field:
from crispy_forms.bootstrap import InlineField
helper.layout = Layout(
InlineField(email, readonly=True),
password,
[...]
)
1.4 FormHelper
What is a FormHelper and how to use it, is throughly explained in a previous section {% crispy %} tag with forms.
When you do this crispy-forms builds a default layout using form.fields for you, so you dont have to manually
list them all if your form is huge. If you later need to manipulate some bits of a big layout, using dynamic layouts its
highly recommended, check Updating layouts on the go.
Also, now the helper is able to cross match the layout with the form instance, being able to search by widget type if
you are using dynamic API.
12
attrs Added in 1.2.0, a dictionary to set any kind of form attributes. Underscores in keys are translated into hyphens.
The recommended way when you need to set several form attributes in order to keep your helper tidy:
{id: form-id, data_id: /whatever}
<form id="form-id" data-id="/whatever" ...>
form_id Specifies form DOM id attribute. If no id provided then no id attribute is created on the form.
form_class String containing separated CSS clases to be applied to form class attribute. The form will always have
by default uniForm class.
form_tag = True It specifies if <form></form> tags should be rendered when using a Layout. If set to False it
renders the form without the <form></form> tags. Defaults to True.
disable_csrf = False Disable CSRF token, when done, crispy-forms wont use {% crsf_token %} tag. This is
useful when rendering several forms using {% crispy %} tag and form_tag = False csrf_token gets
rendered several times.
form_error_title If you are rendering a form using {% crispy %} tag and it has non_field_errors to display, they are rendered in a div. You can set the title of the div with this attribute. Example: Form Errors.
formset_error_title If you are rendering a formset using {% crispy %} tag and it has non_form_errors to
display, they are rendered in a div. You can set the title of the div with this attribute. Example: Formset Errors.
form_style = default Helper attribute for uni_form template pack. Uni-form has two different form styles built-in.
You can choose which one to use, setting this variable to default or inline.
form_show_errors = True Default set to True. It decides wether to render or not form errors. If set to False,
form.errors will not be visible even if they happen. You have to manually render them customizing your template. This allows you to customize error output.
render_unmentioned_fields = False By default django-crispy-forms renders the layout specified if it exists strictly,
which means it only renders what the layout mentions, unless your form has Meta.fields and
Meta.exclude defined, in that case it uses them. If you want to render unmentioned fields (all form fields),
for example if you are worried about forgetting mentioning them you have to set this property to True. It
defaults to False.
render_hidden_fields = False By default django-crispy-forms renders the layout specified if it exists strictly. Sometimes you might be interested in rendering all forms hidden fields no matter if they are mentioned or not. It
defaults to False.
render_required_fields = False By default django-crispy-forms renders the layout specified if it exists strictly.
Sometimes you might be interested in rendering all forms hidden required fields no matter if they are mentioned or not. It defaults to False.
1.4. FormHelper
13
error_text_inline = True Sets whether to render error messages inline or block. If set to True errors will be rendered
using help-inline class, otherwise using help-block. By default error messages are rendered in inline
mode.
html5_required = False When set to True all required fields inputs will be rendered with HTML5
required=required attribute.
form_show_labels = True Default set to True. It decides wether to render or not forms fields labels.
What will happen is that crispy-forms will inject a Django template variable named {{ labels_uppercase }}
with its corresponding value within its templates, including field.html, which is the template in charge of rendering a field when using crispy-forms. So we can go into that template and customize it. We will need to get familiar
with it, but its quite easy to follow, in the end its only a Django template.
When we find where labels get rendered, this chunk of code to be more precise:
{% if not labels_uppercase %}{{ field.label|safe }}{% else %}{{ field.label|safe|upper }}{% endif %}{
Now we only need to override field template, for that you may want to check section Overriding layout objects
templates.
Warning: Be careful, depending on what you aim to do, sometimes using dynamic layouts is a better option,
check section Updating layouts on the go.
14
1.5 Layouts
1.5.1 Fundamentals
Django-crispy-forms defines another powerful class called Layout, which allows you to change the way the form
fields are rendered. This allows you to set the order of the fields, wrap them in divs or other structures, add html,
set ids, classes or attributes to whatever you want, etc. And all that without writing a custom form template, using
programmatic layouts. Just attach the layout to a helper, layouts are optional, but probably the most powerful thing
django-crispy-forms has to offer.
A Layout is constructed by layout objects, which can be thought of as form components.
All these components are explained later in Universal layout objects, what you need to know now about them is that
every component renders a different template and has a different purpose. Lets write a couple of different layouts for
our form, continuing with our form class example (note that the full form is not shown again).
Some layout objects are specific to a template pack. For example ButtonHolder is for uni_form template_pack,
while FormActions is for bootstrap template pack.
Lets add a layout to our helper:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit
class ExampleForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
first arg is the legend of the fieldset,
like_website,
favorite_number,
favorite_color,
favorite_food,
notes
),
ButtonHolder(
Submit(submit, Submit, css_class=button white)
)
)
We will get the fields wrapped in a fieldset, whose legend will be set to first arg is the legend of the fieldset. The fields
order will be: like_website, favorite_number, favorite_color, favorite_food and notes. We
also get a submit button wrapped in a <div class="buttonHolder"> which uni-form CSS positions in a nice
way. That button has its CSS class set to button white.
This is just the tip of the iceberg: now imagine you want to add an explanation for what notes are, you can use HTML
layout object:
Layout(
Fieldset(
Tell us your favorite stuff {{ username }},
1.5. Layouts
15
like_website,
favorite_number,
favorite_color,
favorite_food,
HTML("""
<p>We use notes to get better, <strong>please help us {{ username }}</strong></p>
"""),
notes
)
)
As youll notice the fieldset legend is context aware and you can write it as if it were a chunk of a template where
the form will be rendered. The HTML object will add a message before the notes input and its also context aware.
Note how you can wrap layout objects into other layout objects. Layout objects Fieldset, Div, MultiField and
ButtonHolder can hold other Layout objects within. Lets do an alternative layout for the same form:
Layout(
MultiField(
Tell us your favorite stuff {{ username }},
Div(
like_website,
favorite_number,
css_id = special-fields
),
favorite_color,
favorite_food,
notes
)
)
This time we are using a MultiField, which is a layout object that as a general rule can be used in the same places
as Fieldset. The main difference is that this renders all the fields wrapped in a div and when there are errors in the
form submission, they are shown in a list instead of each one surrounding the field. Sometimes the best way to see
what layout objects do, is just try them and play with them a little bit.
If you want to set html attributes, with words separated by hyphens like data-name, as Python doesnt support
hyphens in keyword arguments and hyphens are the usual notation in HTML, underscores will be translated into
hyphens, so you would do:
Field(field_name, data_name="whatever")
As class is a reserved keyword in Python, for it you will have to use css_class. For example:
Field(field_name, css_class="black-fields")
16
NOTE Mainly in all layout objects you can set kwargs that will be used as HTML attributes. As class is a reserved
keyword in Python, for it you will have to use css_class. For example:
Div(form_field_1, style="background: white;", title="Explication title", css_class="bigdivs")
HTML: A very powerful layout object. Use it to render pure html code. In fact it behaves as a Django template
and it has access to the whole context of the page where the form is being rendered. This layout object doesnt
accept any extra parameters than the html to render, you cannot set html attributes like in Div:
HTML("{% if success %} <p>Operation was successful</p> {% endif %}")
Warning: Beware that this is rendered in a standalone template, so if you are using custom templatetags or filters, dont forget to add your {% load custom_tags %}
Field: Extremely useful layout object. You can use it to set attributes in a field or render a specific field with a
custom template. This way you avoid having to explicitly override the fields widget and pass an ugly attrs
dictionary:
Field(password, id="password-field", css_class="passwordfields", title="Explanation")
Field(slider, template="custom-slider.html")
This layout object can be used to easily extend Djangos widgets. If you want to render a Django form field as hidden
you can simply do:
Field(field_name, type="hidden")
If you need HTML5 attributes, you can easily do those using underscores data_name kwarg here will become into
data-name in your generated html:
Field(field_name, data_name="special")
Fields in bootstrap are wrapped in a <div class="control-group">. You may want to set extra classes in
this div, for that do:
Field(field_name, wrapper_class="extra-class")
Submit: Used to create a submit button. First parameter is the name attribute of the button, second parameter
is the value attribute:
Submit(search, SEARCH)
Submit(search, SEARCH)
Renders to:
1.5. Layouts
17
Button(name, value)
Fieldset: It wraps fields in a <fieldset>. The first parameter is the text for the fieldset legend, as weve said
it behaves like a Django template:
Fieldset("Text for the legend {{ username }}",
form_field_1,
form_field_2
)
MultiField: It wraps fields in a <div> with a label on top. When there are errors in the form submission it
renders them in a list instead of each one surrounding the field:
MultiField("Text for the label {{ username }}",
form_field_1,
form_field_2
)
AppendedText: It renders a bootstrap appended text input. The first parameter is the name of the field to add
appended text to, then the appended text which can be HTML like. There is an optional parameter active, by
default set to False, that you can set to a boolean to render appended text active:
18
PrependedText: It renders a bootstrap prepended text input. The first parameter is the name of the field to add
prepended text to, then the prepended text which can be HTML like. There is an optional parameter active,
by default set to False, that you can set to a boolean to render prepended text active:
PrependedText(field_name, <b>Prepended text</b> to show)
PrependedText(field_name, @, placeholder="username")
PrependedAppendedText: It renders a combined prepended and appended text. The first parameter is the name
of the field, then the prepended text and finally the appended text:
PrependedAppendedText(field_name, $, .00),
InlineRadios:
It renders a Django forms.ChoiceField
forms.RadioSelect using inline radio buttons:
field
with
its
widget
set
to
InlineRadios(field_name)
StrictButton: It renders a button using <button> html, not input. By default type is set to button and
class is set to btn:
StrictButton(Buttons content, name="go", value="go", css_class="extra")
StrictButton(Success, css_class="btn-success")
Tab & TabHolder: Tab renders a tab, different tabs need to be wrapped in a TabHolder for automatic
javascript functioning, also you will need bootstrap-tab.js included in your static files:
1.5. Layouts
19
TabHolder(
Tab(First Tab,
field_name_1,
Div(field_name_2)
),
Tab(Second Tab,
Field(field_name_3, css_class="extra")
)
)
Accordion & AccordionGroup: AccordionGroup renders an accordion pane, different groups need to be
wrapped in an Accordion for automatic javascript functioning, also you will need bootstrap-tab.js
included in your static files:
Accordion(
AccordionGroup(First Group,
radio_buttons
),
Tab(Second Group,
Field(field_name_3, css_class="extra")
)
)
20
Globally: You override the template of the layout object, for all instances of that layout object you use:
from crispy_forms.layout import Div
Div.template = my_div_template.html
Individually: You can override the template for a specific layout object in a layout:
Layout(
Div(
field1,
field2,
template=my_div_template.html
)
)
Overriding templates directory: This means mimicking crispy-forms directory structure into your project and
then copying the templates that you want to override there, finally editing those copies. If you are using this
approach its better to just copy and edit templates you will customize instead of all.
The official layout objects live in layout.py and bootstrap.py, you may want to have a look at them to fully
understand how to proceed. But in general terms, a layout object is a template rendered with some parameters passed.
If you come up with a good idea and design a layout object you think others could benefit from, please open an issue
or send a pull request, so django-crispy-forms gets better.
1.5. Layouts
21
Or:
helper.layout = Layout(
common_layout,
Div(
professional_interests,
job_description,
)
)
We have defined a layout and used it as a chunk of another layout, which means that those two layouts will start the
same way and then extend the layout in different ways.
22
1.6.1 Formsets
Its not the purpose of this documentation to explain how formsets work in detail, for that you should check Django
official formset docs. Lets start creating a formset using the previous ExampleForm form:
from django.forms.models import formset_factory
ExampleFormSet = formset_factory(ExampleForm, extra=3)
formset = ExampleFormSet()
This is how you would render the formset using default rendering, no layouts or form helpers:
{% crispy formset %}
Of course, you can still use a helper, otherwise there would be nothing crispy in this. When using a FormHelper
with a formset compared to when you use it with a form, the main difference is that helper attributes are applied to the
form structure, while the layout is applied to the formsets forms. Lets create a helper for our ExampleFormSet:
class ExampleFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(ExampleFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = post
self.layout = Layout(
favorite_color,
favorite_food,
)
self.render_required_fields = True,
This helper is quite easy to follow. We want our form to use POST method, and we want favorite_color to be
the first field, then favorite_food and finally we tell crispy to render all required fields after. Lets go and use it,
when using {% crispy %} tag in a template there is one main difference when rendering formsets vs forms, in this
case you need to specify the helper explicitly.
This would be part of an hipothetic function view:
formset = ExampleFormSet()
helper = ExampleFormSetHelper()
return render(request, template.html, {formset: formset, helper: helper})
There are two ways you can add submit buttons to a formset. Using FormHelper.add_input method:
helper.add_input(Submit("submit", "Save"))
Or you can set FormHelper.form_tag to False and control the formset outter structure at your will writing
boring HTML:
<form action="{% url save_formset %}" method="POST">
{% crispy formset helper %}
<div class="form-actions">
<input type="submit" name="submit" value="Save" class="btn btn-primary" id="submit-save">
</div>
</form>
Finally, model formsets and inline formsets are rendered exactly the same as formsets, the only difference is how you
build them in your Django code.
23
Basically you can access a forloop Django node, as if you were rendering your formsets forms using a for loop.
The best part is that if this template doesnt do exactly what you want, you can copy it into your templates folder,
customize it and then link your helper to your alternative version. If you think what you are missing would be valuable
to others, then please submit a pull request at github.
Warning: This template doesnt currently take into account any layout you have specified and only works with
bootstrap template pack.
Where every form has a helper attribute from which crispy will grab the layout. In your view you will need to
change the layout or use a different help for every formsets form. Make sure that you have form_tag attribute set
to False, otherwise you will get 3 individual forms rendered.
24
You can basically do all kind of slices, the same ones supported by Pythons lists. You can also concatenate them. If
you had this layout:
Layout(
Div(email)
)
1.7.2 wrap
One useful action you can apply on a slice is wrap, which wraps every selected field using a layout object type and
parameters passed. Lets see an example. If We had this layout:
Layout(
field_1,
field_2,
field_3
)
We could do:
form.helper[1:3].wrap(Field, css_class="hello")
Note how wrap affects each layout object selected, if you would like to wrap field_2 and field_3 together in a
Field layout object you will have to use wrap_together.
Beware that the slice [1:3] only looks in the first level of depth of the layout. So if the previous layout was this way:
Layout(
field_1,
Div(field_2),
field_3
)
25
Parameters passed to wrap or wrap_together will be used for creating the layout object that is wrapping selected
fields. You can pass args and kwargs. If you are using a layout object like Fieldset which needs a string as
compulsory first argument, wrap will not work as desired unless you provide the text of the legend as an argument to
wrap. Lets see a valid example:
form.helper[1:3].wrap(Fieldset, "legend of the fieldset")
1.7.3 wrap_together
wrap_together wraps a whole slice within a layout object type with parameters passed. Lets see an example. If
We had this layout:
Layout(
field_1,
field_2,
field_3
)
We could do:
form.helper[0:3].wrap_together(Field, css_class="hello")
1.7.4 update_attributes
Updates attributes of every layout object contained in a slice:
Layout(
field_1,
Field(field_2),
Field(field_3)
)
We could do:
form.helper[0:3].update_attributes(css_class="hello")
26
Because it would change Layout attrs. Its your job to have it wrapped correctly.
1.7.5 all
This method selects all first level of depth layout objects:
form.helper.all().wrap(Field, css_class="hello")
If we do:
form.helper[password].wrap(Field, css_class="hero")
1.7.7 filter
This method will allow you to filter layout objects by its class type, applying actions to them:
form.helper.filter(basestring).wrap(Field, css_class="hello")
form.helper.filter(Div).wrap(Field, css_class="hello")
You can filter several layout objects types at the same time:
form.helper.filter(basestring, Div).wrap(Div, css_class="hello")
27
By default filter is not greedy, so it only searches first depth level. But you can tune it to search in different levels
of depth with a kwarg max_level (By default set to 0). Let see some examples, to clarify it. Imagine we have this
layout:
Layout(
field_1,
Div(
Div(password)
),
field_3
)
If we did:
form.helper.filter(basestring).wrap(Field, css_class="hello")
If we wanted to search deeper, wrapping password, we would need to set max_level to 2 or more:
form.helper.filter(basestring, max_level=2).wrap(Field, css_class="hello")
In other words max_level indicates the number of jumps crispy-forms can do within a layout object for matching.
In this case getting into the first Div would be one jump, and getting into the next Div would be the second jump,
thus max_level=2.
We can turn filter greedy, making it search as deep as possible, setting greedy to True:
form.helper.filter(basestring, greedy=True).wrap(Div, css_class="hello")
Parameters:
max_level: An integer representing the number of jumps that crispy-forms should do when filtering. Defaults
to 0.
greedy: A boolean that indicates whether to filter greedy or not. Defaults to False.
1.7.8 filter_by_widget
Matches all fields of a widget type. This method assumes you are using a helper with a form attached, see section
FormHelper with a form attached (Default layout), you could filter by widget type doing:
form.helper.filter_by_widget(forms.PasswordInput).wrap(Field, css_class="hero")
filter_by_widget is greedy by default, so it searches in depth. Lets see a use case example, imagine we have
this Layout:
Layout(
username,
Div(password1),
Div(password2)
)
28
Supposing password1 and password2 fields are using widget PasswordInput, would turn into:
Layout(
username,
Div(Field(password1, css_class="hero")),
Div(Field(password2, css_class="hero"))
)
An interesting real use case example here would be to wrap all SelectInputs with a custom made ChosenField
that renders the field using a chosenjs compatible field.
1.7.9 exclude_by_widget
Excludes all fields of a widget type. This method assumes you are using a helper with a form attached, see section
FormHelper with a form attached (Default layout):
form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class="hero")
exclude_by_widget is greedy by default, so it searches in depth. Lets see a use case example, imagine we have
this Layout:
Layout(
username,
Div(password1),
Div(password2)
)
Supposing password1 and password2 fields are using widget PasswordInput, would turn into:
Layout(
Field(username, css_class="hero"),
Div(password1),
Div(password2)
)
This is how you would add one layout object at the end of the Layout:
layout.append(HTML("<p>whatever</p>"))
This is how you would add one layout object at the end of another layout object:
layout[0].append(HTML("<p>whatever</p>"))
29
layout.extend([
HTML("<p>whatever</p>"),
Div(add_field_on_the_go)
])
This is how you would add several layout objects to another layout object:
layout[0][2].extend([
HTML("<p>whatever</p>"),
Div(add_field_on_the_go)
])
This is how you would delete the second layout object within the Layout:
layout.pop(1)
This is how you wold delete the second layout object within the second layout object:
layout[1].pop(1)
This is how you would insert a layout object in the second position of a Layout:
layout.insert(1, HTML("<p>whatever</p>"))
This is how you would insert a layout object in the second position of the second layout object:
layout[1].insert(1, HTML("<p>whatever</p>"))
Warning: Remember always that if you are going to manipulate a helper or layout in a view or any part of your
code, you better use an instance level variable.
1.8.1 Technical
Displaying columns in a boostrap Layout
Of course you can display form fields within columns, the same way you would do it in a standard bootstrap form,
you can do it in a bootstrap layout, see an example.
30
1.8.2 General
Why use django-crispy-forms and not other app
Well, Im obviously biased for answering this question. But I once answered it at StackOverflow.
How did this all get started?
In December 2008, while Daniel Greenfeld was working for NASAs Science Mission Directorate, his team began to
use Django and Pinax. There was a necessity to make all the forms in Pinax Section 508 compatible, and the thought
of going through all of forms and rewriting {{ form }} as a block of {% for field in form %} with all
the template logic seemed like way too much work.
So with the encouragement of Katie Cunningham, James Tauber and Jannis Leidel Daniel took the Django docs
on forms and combined it with Dragan Babics excellent Uni-Form css/javascript library and created the ubiquitous
as_uni_form filter. After that, fixing all the forms in Pinax to be section 508 compliant was trivial.
Not long before PyCon 2009 James Tauber suggested the {% uni_form form helper %} API, where one
could trivially create forms without writing any HTML.
At PyCon 2009 Jannis Leidel helped Daniel through releasing the 0.3 release of django-uni-form on PyPI. It was also
at that PyCon when the project moved from Google Code to Github.
Around January 2011 the project wasnt very active, Github issues and forks were stacking up. At that time Miguel
Araujo found django-uni-form and loved the concept behind its architecture. He started working in a fork of the
project, trying to gather some old submitted patches. Around march of 2011, after conversations with Daniel, he got
commit powers in the projects repository, reactivating dev branch. Releases 0.8.0, 0.9.0 followed and the project more
than doubled its watchers in Github.
By the end of 2011, Miguel and Daniel agreed on the necessity of renaming the project. As uni-form CSS framework
was not anymore the only option available and the name was confusing the users. Thus django-crispy-forms was
borned, named by Audrey Roy. The project is now actively maintained and leaded by Miguel Araujo.
How fast is django-crispy-forms
Performance in form rendering is normally a nigh moot issue in Django, because the majority of speed issues are
fixable via appropriate use of Djangos cache engine. Templates and especially form rendering are usually the last
things to worry about when you try to increase performance.
However, because we do care about producing lean and fast code, work is being done to speed up and measure
performance of this library. These are the average times of rendering 1000 forms with the latest django-crispy-forms
code in Dell Latitude E6500 Intel Core 2 Duo @ 2.53GHz.
In production environments, you will want to activate template caching, see Installing django-crispy-forms.
Method
Plain Django
|crispy filter
{% crispy %} tag
31
32
CHAPTER 2
API documentation
If you are looking for information on a specific function, class or method, this part of the documentation is for you.
Lets see what attributes you can set and what form behaviors they apply to:
form_method: Specifies form method attribute. You can see it to POST or GET. Defaults to
POST
form_action: Applied to the form action attribute:
Can be a named url in your URLconf that can be executed via the {% url %} template tag.
Example: show_my_profile. In your URLconf you could have something like:
url(r^show/profile/$, show_my_profile_view, name = show_my_profile)
33
add_input(input): You can add input buttons using this method. Inputs added
method will be rendered at the end of the form/formset.
using
this
add_layout(layout): You can add a Layout object to FormHelper. The Layout specifies in a
simple, clean and DRY way how the form fields should be rendered. You can wrap fields, order
them, customize pretty much anything in the form.
Best way to add a helper to a form is adding a property named helper to the form that returns customized
FormHelper object:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class MyForm(forms.Form):
title = forms.CharField(_("Title"))
@property
def helper(self):
helper = FormHelper()
helper.form_id = this-form-rocks
helper.form_class = search
helper.add_input(Submit(save, save))
[...]
return helper
get_attributes(template_pack=bootstrap)
Used by crispy_forms_tags to get helper attributes
render_layout(form, context, template_pack=bootstrap)
Returns safe html of the rendering of the layout
Note: The first argument is also slugified and turned into the id for the button.
class layout.ButtonHolder(*fields, **kwargs)
Layout object. It wraps fields in a <div class=buttonHolder>
This is where you should put Layout objects that render to form buttons like Submit. It should only hold HTML
and BaseInput inherited objects.
34
Example:
ButtonHolder(
HTML(<span style="display: hidden;">Information Saved</span>),
Submit(Save, Save)
)
The first parameter is the text for the fieldset legend. This text is context aware, so you can do things like:
Fieldset("Data for {{ user.username }}",
form_field_1,
form_field_2
)
class layout.HTML(html)
Layout object. It can contain pure HTML and it has access to the whole context of the page where the form is
being rendered.
Examples:
HTML("{% if saved %}Data saved{% endif %}")
HTML(<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />)
35
You need to add your Layout to the FormHelper using its method add_layout.
Example:
layout = Layout(
Fieldset(Company data,
is_company
),
Fieldset(_(Contact details),
email,
Row(password1, password2),
first_name,
last_name,
HTML(<img src="/media/somepicture.jpg"/>),
company
),
ButtonHolder(
Submit(Save, Save, css_class=button white),
),
)
helper.add_layout(layout)
Note: The first argument is also slugified and turned into the id for the reset.
class layout.Row(*fields, **kwargs)
Layout object. It wraps fields in a div whose default class is formRow. Example:
Row(form_field_1, form_field_2, form_field_3)
Note: The first argument is also slugified and turned into the id for the submit button.
36
tem-
render(context)
class templatetags.crispy_forms_tags.ForLoopSimulator(formset)
Simulates a forloop tag, precisely:
{% for form in formset.forms %}
If {% crispy %} is rendering a formset with a helper, We inject a ForLoopSimulator object in the context as
forloop so that formset forms can do things like:
Fieldset("Item {{ forloop.counter }}", [...])
HTML("{% if forloop.first %}First form text{% endif %}"
iterate()
Updates values as if we had iterated over the for
templatetags.crispy_forms_tags.copy_context(context)
Copies a Context variable. It uses Context.__copy__ if available (introduced in Django 1.3) or copy otherwise.
templatetags.crispy_forms_tags.do_uni_form(parser, token)
You need to pass in at least the form/formset object, and can also pass in the optional
crispy_forms.helpers.FormHelper object.
helper (optional): A crispy_forms.helper.FormHelper object.
Usage:
{% include crispy_tags %}
{% crispy form form.helper %}
37
You can also provide the template pack as the third argument:
{% crispy form form.helper bootstrap %}
templatetags.crispy_forms_tags.whole_uni_form_template(*args)
templatetags.crispy_forms_tags.whole_uni_formset_template(*args)
templatetags.crispy_forms_filters.as_crispy_errors(form,
plate_pack=bootstrap)
Renders only form errors the same way as django-crispy-forms:
tem-
{% load crispy_forms_tags %}
{{ form|as_crispy_errors }}
or:
{{ form|as_crispy_errors:"bootstrap" }}
templatetags.crispy_forms_filters.as_crispy_field(field, template_pack=bootstrap)
Renders a form field like a django-crispy-forms field:
{% load crispy_forms_tags %}
{{ form.field|as_crispy_field }}
or:
{{ form.field|as_crispy_field:"bootstrap" }}
templatetags.crispy_forms_filters.flatatt_filter(attrs)
templatetags.crispy_forms_filters.uni_form_template(*args)
templatetags.crispy_forms_filters.uni_formset_template(*args)
class templatetags.crispy_forms_field.CrispyFieldNode(field, attrs)
render(context)
38
templatetags.crispy_forms_field.classes(field)
Returns CSS classes of a field
templatetags.crispy_forms_field.crispy_addon(field, append=, prepend=)
Renders a form field using bootstraps prepended or appended text:
{% crispy_addon form.my_field prepend="$" append=".00" %}
39
40
CHAPTER 3
Developer Guide
Think this is awesome and want to make it better? Read our contribution page, make it better, and you will be added
to the contributors list!
3.1 Contributing
3.1.1 Setup
Fork on github
Before you do anything else, login/signup
https://github.com/maraujop/django-crispy-forms.
on
Github.com
and
fork
django-crispy-forms
from
41
You should use a verbose enough name for your branch so it is clear what it is about. Now you can commit your
changes and regularly merge in the upstream master as described below.
When you are ready to generate a pull request, either for preliminary review, or for consideration of merging into the
project you must first push your local topic branch back up to github:
git push origin fix-broken-thing
Now when you go to your fork on github, you will see this branch listed under the Source tab where it says Switch
Branches. Go ahead and select your topic branch from this list, and then click the Pull request button.
Here you can add a comment about your branch. If this in response to a submitted issue, it is good to put a link to
that issue in this initial comment. The repo managers will be notified of your pull request and it will be reviewed
(see below for best practices). Note that you can continue to add commits to your topic branch (and push them up to
github) either if you see something that needs changing, or in response to a reviewers comments. If a reviewer asks
for changes, you do not need to close the pull and reissue it after making changes. Just make the changes locally, push
them to github, then add a comment to the discussion section of the pull request.
Check the log to be sure that you actually want the changes, before merging:
git log ..django-crispy-forms/master
The first thing the core committers will do is run this command. Any pull request that fails this test suite will be
rejected.
42
Please keep your code as clean and straightforward as possible. When we see more than one or two functions/methods
starting with _my_special_function or things like __builtins__.object = str we start to get worried. Rather than try and
figure out your brilliant work well just reject it and send along a request for simplification.
Furthermore, the pixel shortage is over. We want to see:
helper instead of hpr
django-crispy-forms instead of dcf
my_function_that_does_things instead of mftdt
3.1. Contributing
43
44
h
helper, 33
l
layout, 34
t
templatetags.crispy_forms_field, 38
templatetags.crispy_forms_filters, 38
templatetags.crispy_forms_tags, 37
45