Djangoのテンプレートに、Bootstrapのform-groupやform-controlを設定できないとき

DjangoでBootstrapをFormにうまく適用させられず、調べたのでメモ。

問題点

Modelから生成したフォーム(forms.py)を使ってテンプレートにフォームを表示させる際に、{{ form }}の形で自動生成している。

<div class="container" style="padding:20px 0">
  <form method="post" enctype="multipart/form-data" action="">
    {% csrf_token %}
    {{ form }}
    <div class="form-group text-right" style="padding:20px 0">
      <input type="submit" value="Submit" class="btn btn-primary"/>
    </div>
  </form>
</div>

その場合、<label><input>がないのでBootstrap固有のform-groupform-controlを設定できず、下のような素のフォームとなってしまう。
f:id:kita83:20170817224821p:plain

for文で普通にフォームを作成してみた

<div class="container" style="padding:20px 0">
  <form method="post" enctype="multipart/form-data" action="">
    {% csrf_token %}
    {% for field in form %}
      <div class="form-group">
        <label class="control-label" for="{{ field.name }}">{{ field.label }}</label>
        <div class="form-control">
          <input type="" class="form-control"
                 name="{{ field.name }}"
                 id="{{ field.name }}">
      </div>
    </div>
    {% endfor %}
    <div class="form-groupe text-right" style="padding:20px 0">
      <input type="submit" value="Submit" class="btn btn-primary"/>
    </div>
  </form>
</div>

うまく表示された。
f:id:kita83:20170817225833p:plain

これで正しく動作もしているようなんだけど、<input type="">って何も入れなくていいのか..
フィールドタイプが全て同じではないのでfield.***と動的に変えたいがそういったものがあるのか、指定の仕方がわからない。

結果的にforms.pyで指定できた

__init__で全てのフォームの部品のclassにform-controlを指定することで適用することができた。

class RestaurantForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(RestaurantForm, self).__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs["class"] = "form-control"

    class Meta:
        model = Restaurant
        exclude = ('user', 'date',)

上記のように、

for field in self.fields.values():
    field.widget.attrs["class"] = "form-control"

を追加して、{{ form }}だけの書き方に戻すと..

<div class="container" style="padding:20px 0">
  <form method="post" enctype="multipart/form-data" action="">
    {% csrf_token %}
    {{ form }}
    <div class="form-group text-right" style="padding:20px 0">
      <input type="submit" value="Submit" class="btn btn-primary"/>
    </div>
  </form>
</div>

初めの素のフォームではなく、Bootstrapが適用されるようになりました。

f:id:kita83:20170817231219p:plain

django-bootstrap-toolkit という便利ツールもあるよう

このツールを導入すると{{ form|as_bootstrap }}でいけるみたいです。

↓参考

Djangoで、ModelForm、Formのあれこれ - naritoブログ

DjangoでTwitterBootstrap使うならdjango-boostrap-toolkitがオススメ - Make組ブログ(移行して http://blog.hirokiky.org/ にあるよ)

<広告>
英語だけど、Djangoについてここまで体系的にまとめられているのってあまり無いんじゃないでしょうか。

Two Scoops of Django: Best Practices for Django 1.8

Two Scoops of Django: Best Practices for Django 1.8