Django Forms 和 Bootstrap-CSS 类以及 < div >

我使用 Bootstrap和 Django 来渲染表单。

Bootstrap可以很好地格式化表单——只要你有它期望包含的 CSS类。

但是,我的问题是 Django 的 {{ form.as_p }}生成的表单不能很好地用 Bootstrap 呈现,因为它们没有这些类。

例如,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>

据我所知,Bootstrap 要求你的表单有一个 <fieldset class="control-group">,每个 <label>有一个 class="control-label",并且每个 <input>都包装在一个 <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>

然而,将自定义 CSS 标签添加到 Django 中的每个表单字段是相当痛苦的:

将类添加到 Django label _ tag () output

有没有一种更聪明的方法,既可以使用 {{ form.as_p }},也可以迭代这些字段,而不必手动指定内容,或者进行大量的黑客操作?

干杯, 维克多

97168 次浏览

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.

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

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.

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)

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>

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">

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.

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.

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>

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

After going back and forth, and searching docs and stackoverflow answers, this is what I have created which worked for me.

I checked the docs for the django-crispy-forms and found that it doesn't support Bootstrap 5.0 +.

Forms.py

from django.contrib.auth.forms import UserCreationForm
from .models import MyModel
class RegistrationForm(UserCreationForm):
class Meta:
model = MyModel
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')

Views.py

from .models import MyModel
from .forms import RegistrationForm


def signup(request):
if request.method == 'POST':
r_form = RegistrationForm(request.POST)
if r_form.is_valid():
r_form.save()
return redirect('home')
else:
r_form = RegistrationForm()


context = {
'form': r_form
}
return render(request, 'myapp/signup.html', context)

Signup.html (for loop method)

<form action="/signup/" method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-floating">
<input
type="\{\{ field.field.widget.input_type }}"
class="form-control"
id="\{\{ field.id_for_label }}"
name="\{\{field.name}}"
required
/>
<label for="\{\{field.id_for_label}}">\{\{field.label}}</label>
</div>
{% endfor %}


<div class="d-grid gap-2 d-md-block py-2">
<button class="btn btn-primary" type="submit">Sign up</button>
</div>
</form>

Signup.html (manual method)

<form action="/signup/" method="post">
{% csrf_token %}
<div class="form-floating my-1">
<input
type="email"
class="form-control"
id="\{\{form.email.id_for_label}}"
name="email"
required
/>
<label for="\{\{form.email.id_for_label}}">Email address</label>
</div>
... next div

I have used this here bootstrap example, you may need to configure the css styles.

References