【问题标题】:Django Forms and Bootstrap - CSS classes and <divs>Django Forms and Bootstrap - CSS 类和 <divs>
【发布时间】:2012-01-18 10:51:34
【问题描述】:

我正在使用Twitter 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 要求您的表单有一个&lt;fieldset class="control-group"&gt;,每个&lt;label&gt; 有一个class="control-label",每个&lt;input&gt; 都包含在一个&lt;div&gt; 中:

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

但是,在 Django 中为每个表单字段添加自定义 CSS 标签是相当痛苦的:

Add class to Django label_tag() output

有没有更聪明的方法来使用{{ form.as_p }},或者遍历字段,而无需手动指定事物,或者做一大堆hacky?

干杯, 维克多

【问题讨论】:

标签: css django twitter-bootstrap


【解决方案1】:

最快和最简单的方法是定义你自己的扩展 Django Form 类的基类,并重新定义其as_p 方法以以 Bootstrap 所需的格式输出。然后将表单更改为从新的 Form 类而不是 Django 类继承。

【讨论】:

    【解决方案2】:

    你可以这样做:

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

    【讨论】:

    • 参见:form.hidden_​​fields 和 form.visible_fields
    【解决方案3】:

    我喜欢使用"django-crispy-forms",它是 django-uni-form 的继承者。这是一个很棒的小 API,并且对 Bootstrap 有很好的支持。

    我倾向于使用模板过滤器来快速移植旧代码和快速表单,当我需要更多地控制渲染时使用模板标签。

    【讨论】:

    • 我认为这是 2014 年的“正确”答案。
    • 我刚刚检查了 django-crispy-forms github 页面,它看起来很活跃。所以这可能是一个不错的赌注。 (不过,我对此并不了解。)
    • 我不喜欢布局在 django-crispy-forms 中的 python 代码中的事实。
    • 回购在 2017 年看起来仍然很活跃
    【解决方案4】:

    【讨论】:

    • 此工具已停用。不再相关。
    【解决方案5】:

    例如,您可以创建一个类,该类以您想要的方式定义属性并相应地调用它。

    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)
    

    【讨论】:

      【解决方案6】:

      这是我想出的:

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

      【讨论】:

      • 太棒了!谢谢。我还希望最终能够使标签成为连接到文本框侧面的跨度。我想我需要安装一些其他的包来管理它。
      • 如果您需要跨多个模板使用它,请使用reusable form template
      • 太棒了,很有用!
      【解决方案7】:

      为了给 Django 生成的表单添加 CSS 属性,在你的 forms.py 中使用以下代码就足够了:

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

      它将生成以下 HTML 代码:

      <label for="id_Recepient">Recepient</label>
      <select id="combobox" name="Recepient">
      

      【讨论】:

        【解决方案8】:

        django-crispy-forms 无法使用时(例如,当模板单独处理表单的每个字段时),jcmrgo 的答案是唯一的方法。根据他的回答,这里是 Bootstrap 3 的解决方案(将他的版本留给 Boostrap 2),并带有 模板内的字段类调整。虽然使用 Django 的标准库无法从模板内访问字段类(这会导致其他解决方案中出现额外的表单或模板标签),但这里有一个解决方案,它可以将正确的类设置为字段标签,而无需在模板之外编写代码:

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

        这需要安装django-widget-tweaks 并将widget_tweaks 添加到INSTALLED_APPS

        【讨论】:

        • 截至 2015 年,这是最好的答案。特别是如果您赶时间(需要交付)并且没有心情/没有时间学习另一个库,例如脆表格。非常感谢先生!
        • 截至 2016 年,这仍然是无需学习清晰即可轻松集成的最佳答案。
        • 这确实应该是默认答案。 django-crispy-forms 如果您不想模板化您的表单并且有时间使用需要时间学习的大型库,那么 django-crispy-forms 非常好。但是,在原始问题的背景下并以实施时间的名义;这是最好的答案。
        • 要设置占位符 - 如果在表单中定义了一个 - 你可以另外做placeholder="{{ field.field.widget.attrs.placeholder }}。 Django 也可以自己提供所需的idid="{{ field.id_for_label }}"name="{{ field.html_name }} 的名称
        • 截至 2018 年,这仍然是最好的答案。我喜欢它在模板中保留演示文稿的内容,而不是清晰的表单,这迫使我将其放入 python 代码中。而且,我喜欢启动和运行相当简单。我唯一的建议是使用“渲染字段”而不是过滤器,下面的另一个答案中有更详细的描述。
        【解决方案9】:

        使用&lt;div&gt;s 而不是&lt;p&gt;s 的引导样式表单。所以,如果你想让它看起来不错,你需要 100% 恕我直言。这是我喜欢的做法:

        使用django-bootstrap3 应用程序。示例:

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

        请注意 1) 表单类属性 2) bootstrap_form 布局和 3) 按钮布局中的 horizontal

        【讨论】:

        • 我认为这是最优雅的方法,也许最好补充一下,您也可以像这样渲染单个字段:{% bootstrap_field form.field_name %} 并能够添加表单集等。
        【解决方案10】:

        这是我使用 django_tweaks 的版本,效果更好。我发现 render_field 比添加过滤器更容易使用。我还添加了引导格式的警报消息并关闭了导航器验证(使用 novalidate)。我对 Django 比较陌生,所以如果您发现任何废话,请随时发表评论

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

        【讨论】:

          【解决方案11】:

          除了其他朋友说的,我推荐使用'django-widget-tweaks'。

          答案是这样的:

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

          【讨论】:

            【解决方案12】:

            在来回搜索文档和 stackoverflow 答案之后,这就是我创建的对我有用的内容。

            我检查了 django-crispy-forms 的文档,发现它不支持 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循环方法)

            <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(手动方法)

            <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
            

            我用过这个here bootstrap 例子,你可能需要配置css样式。

            参考文献

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-03-09
              • 2015-08-03
              • 1970-01-01
              • 1970-01-01
              • 2013-08-01
              • 1970-01-01
              • 2014-05-19
              • 2013-10-19
              相关资源
              最近更新 更多