situation
for now, the tag {% bootstrap_form %}
create an horizontal OR inline form.
<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
cons
But what if we want two field on the same line for readability purpose ? we then must stop use {% bootstrap_form %}
and switch to a {% bootstrap_field %}
for each fields and create our line with .row and .col-md-6.
<form role="form" method="post">
{% csrf_token %}
<div class="row">
<div class="col-md-6">
{% bootstrap_field form.subject %}
</div>
<div class="col-md-6">
{% bootstrap_field form.password %}
</div>
</div>
{% bootstrap_field form.message %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
this is a probleme as soon as the form is a little dynamic and when some field can be missing (ie because of user perms). so you will soon need to add some {% if %}
that should not be used if {% bootstrap_form %}
was used
<form role="form" method="post">
{% csrf_token %}
<div class="row">
<div class="col-md-6">
{% bootstrap_field form.subject %}
</div>
<div class="col-md-6">
{% bootstrap_field form.password %}
</div>
</div>
{% bootstrap_field form.message %}
{% if form.maybemissing %}
{% bootstrap_field form.maybemissing %}
{% endif %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
if you don't use {% if %}
you will have the BootstrapError: Parameter "field" should contain a valid Django BoundField.
enhancement
it seem that the layout of {% bootstrap_form %}
should be more powerfull and allow us to create a real layout, not just «horizontal» or «inline».
so, we can have in form.py
class MyLayoutForm(forms.Form):
layout = [
("subject", "password"),
"message",
"maybemissing"
]
subject = forms.CharField(
max_length=100,
help_text='my_help_text',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'placeholdertest'}),
)
password = forms.CharField(widget=forms.PasswordInput)
message = forms.CharField(required=False, help_text='<i>my_help_text</i>')
maybemissing = forms.EmailField(
label='Sender © unicode',
help_text='E.g., "[email protected]"')
def __init__(self, *args, **kwargs):
super(MyLayoutForm, self).__init__(*args, **kwargs)
if whatever:
self.fields.pop("maybemissing")
and keep the simple {% bootstrap_form %}
in our template, as desired :
<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
by adding the "layout" attribute of the Form, the {% bootstrap_form %}
will use it for the form rendering. if none layout is given, it will create one with all fields, which will render exactly the same way as before.
the layout
will allow to create the .row and .col-md-x recursively.
It will take 2 possible type
list and tuple
so, to get the following form, we can just add a list with tuples :
class MyLayoutForm(forms.Form):
layout = [
("subject", "password"),
"message",
"maybemissing"
]
...
Col and Row
the col and Row are the final step of the layout processing. all given layout will be computed in Col and Row. the {% bootstrap_form %}
will use it to render the correct layout.
The Col class
Col(unicode: fieldname, unicode: class_="col-md-x", *rows)
the Col represent a <div class="col-md-xx">
. it can contains other rows or a field of the form (by his name)
Col("message")
Col("message", class_="col-md-6 red")
Col(
Row("message"),
Row("password")
)
the Row class
Row(*cols_or_fields, **field_config)
a Row can contains many Col, or the name of the fields. the related Col will be created to give equal size of all fields. the desired class can be given with a dict like construction. where the name of the field is the key, and the class is the value
the followings lines are equivalent :
Row("message", "subject")
Row(message="col-md-6", subject="col-md-6")
Row(Col("message", class_="col-md-6"), Col("subject", class_="col-md-6"))
a Row should always contains Col and Col can contains Row or the field itself. a Row can be used as :
layout = [
Row(subject="col-md-8 col-xs-6", password="col-md-4 col-xs-6"),
Row("message"),
Row(maybemissing="col-md-12"),
]
Col and Row mixed up with list and tuples
the list constuction is compiled in Row/Col construction.
the 3 following initialization will be identical.
layout = (
("message", "subject"),
("mybemissing",)
)
layout = Col(
("message", "subject"),
("mybemissing",)
)
layout = Col(
Row("message", "subject"),
Row("mybemissing",)
)
Col and Row recursivity
you can add row into cols and cols into Row without deep limitation
layout = Row(
Col(Row(Col("subject")), Row(Col("message"))),
Col("password", "maybemissing"),
)
the last hope
with this logic, it can even be possible to add more layout class with Row and Col, like Tab, Fieldset or Accordion.