Djangoで使用しているHTMLファイルをBootstrapで整える

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

今回は、Djangoで使用しているHTMLファイルに対してBootstrapを使いレイアウトを整える方法をそれっぽく勉強し、なんとなくメモ書きしておこうと思います。
今回はホーム画面とログイン画面にBootstrapを適用していきたいと思います。

# Bootstrapとは
以下Wikipediaと公式サイトとなります。

https://ja.wikipedia.org/wiki/Bootstrap

https://getbootstrap.jp/

Wikipediaには以下のように紹介されています。
小難しく書かれていますが、レイアウトを整えるテンプレートを色々用意してくれているものがBootstrapというざっくりした認識を持ちました。

>BootstrapはウェブサイトやWebアプリケーションを作成するフロントエンドWebアプリケーションフレームワークである。タイポグラフィ、フォーム、ボタン、ナビゲーション、その他構成要素やJavaScript用拡張などが、HTMLおよびCSSベースのデザインテンプレートとして用意されている。

基本的にHTMLとそのレイアウトを決めるCSSは同じサーバ上にあるものだと思っています。
※ここに書いていないけどJavascriptのファイルもCSSと同様の認識です。フロント側の知識はほとんどないので間違っているかもですが。
![django-ページ6.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/723af45f-a347-38df-9471-26c485a41818.png)
Bootstrapを使うとCSSを外部から使用出来るみたいな感じなのかな？
最もローカルに置くこともできるみたいですけれども。。。
![django-ページ7.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/70ddc66e-ddbc-a959-d513-cf5bb5743d7a.png)

# 前回までの記事

https://qiita.com/ohtsuka-shota/items/5f980e12e5d7132c604a

https://qiita.com/ohtsuka-shota/items/bd4d5cebc3e47906a4fa

# レイアウトを整えてみる
## ホーム画面
Bootstrap適用前は以下のように表示がされておりました。
![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_3219385_749cfdd5-5c8a-d60b-0f24-7f5f11540a06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/bf3b8345-1be4-3eb0-dd45-932aba7a2d11.png)
HTMLのコードは以下のようになっています。
レイアウトについて何も考慮していないファイルとなっています。
```home.html
<h1>Hello devProject</h1>
<a href="{% url 'login' %}">ログイン</a>
<a href="{% url 'admin:index' %}">ADMIN</a>
<a href="{% url 'polls:polls' %}">polls</a>
```

これに対してBootstrapを適用していきます。
まず更新したコードが以下となります。
```home.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport", content="width=device-width">
        <title>home画面</title>
        <!-- Bootstrap -->
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    </head>

    <body>
        <h1>Hello devProject</h1>
        <button type="button" class="btn btn-primary"><a href="{% url 'login' %}" "#" class="link-light">Login</a></button>
        <button type="button" class="btn btn-danger"><a href="{% url 'admin:index' %}" "#" class="link-light">Admin</a></button>
        <button type="button" class="btn btn-warning"><a href="{% url 'polls:polls' %}" "#" class="link-light">Polls</a></button>
    </body>

</html>
```
それぞれのコードの意味を以下にメモっておきます。

<br>

```html
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
```

 これは読み込むスタイルシート、今回の場合はBootstrapに用意されているスタイルシートを指定しています。
 公式サイトのドキュメントに書かれているコードをそのままコピペしました。

 https://getbootstrap.jp/docs/5.3/getting-started/introduction/#%e3%81%99%e3%81%90%e3%81%ab%e5%a7%8b%e3%82%81%e3%82%8b

<br>

```html
<button type="button" class="btn btn-primary"><a href="{% url 'login' %}" "#" class="link-light">Login</a></button>
<button type="button" class="btn btn-danger"><a href="{% url 'admin:index' %}" "#" class="link-light">Admin</a></button>
<button type="button" class="btn btn-warning"><a href="{% url 'polls:polls' %}" "#" class="link-light">Polls</a></button>
```
既存で使用していたa要素（アンカー要素）を囲うようにbutton要素を記述しています。
button要素の記述の仕方は以下を参考にしています。

https://getbootstrap.jp/docs/5.3/components/buttons/#%e3%83%9c%e3%82%bf%e3%83%b3%e3%82%bf%e3%82%b0

また、文字の色がデフォルトの状態だと見づらかったので、白文字に変更しています。この変更はa要素に記述することで可能としています。
記述の仕方は以下を参考にしています。CSSを全く用意していなくてもBootstrapが用意している通りに記載することで簡単にレイアウトを調整することが出来るので、簡単でいいですね。

https://getbootstrap.jp/docs/5.0/helpers/colored-links/

上記のようにBootstrapを適用した結果が以下となります。ボタンが表示されて押せばどっかに飛べそうなことがぱっと見で分かるようになりましたね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/3e897e3d-7ef9-91b4-03ec-130a337c2042.png)


## ログイン画面
ここではBootstrapのサイトだけではなく、以下のサイト様を参考にしています。
ありがとうございます。
どうもDjangoで用意しているform.as_pを使っているHTMLファイルのレイアウトを調整する場合、form.as_pの構成要素を色々分解して記述をしないといけないようです。
んーこの辺りは今後も勉強が必要そうです。

https://n-guitar.hatenablog.com/entry/2021/02/03/195712

Bootstrap適用前は以下のように表示がされておりました。
![image37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/b3c4ea87-d621-91e4-7fb5-d16a4a04ac6b.png)

HTMLのコードは以下のようになっていました。

```login.html
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}   
    <button type="submit">ログイン</button>
```

上記サイトとBootstrapに用意されているサンプルの"Sign-in"を参考に作ってみます。
サンプルは以下から確認が出来ます。

https://getbootstrap.jp/docs/5.3/examples/

サンプルに用意されているものはいったんダウンロードして解凍してからでないとコードを確認できないようです。
![Bootstrap templates (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/d8a763c6-bf2c-4e06-d387-0f75d0edf200.png)

更新したHTMLは以下となります。
HTML要素と、Django要素のものを紐解いて理解していく必要がありそうです。
この辺りは個別に試験的なことをしてみないとピンとこないですね。。。💦
```login.html
{% block HeadContent %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Signin</title>
<style>
    .login-form {
        width: 340px;
        margin: 100px auto;
        font-size: 15px;
    }
    .form-group {
        margin-bottom: 1rem;
    }
</style>

<!-- Bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
{% endblock HeadContent %}

{% block BodyContent %}
<div class="login-form">
    <dev class="text-center">
        <form style="max-width:300px;margin:auto" action="" method="POST"> {% csrf_token %}
            <img class="mb-4" src="https://getbootstrap.jp/docs/5.3/assets/brand/bootstrap-logo.svg" alt="" width="72" height="57">
            <h1 class="h3 mb-3 fw-normal">Please sign in</h1>

            <div class="form-group">
                <input class="form-control" type="text" placeholder="Username"
                name="{{ form.username.html_name }}" autofocus="" autocapitalize="none"
                autocomplete="{{ form.username.html_name }}" maxlength="150"
                required="" id="{{ form.username.id_for_label }}">
            </div>

            <div class="form-group">
                <input class="form-control" type="password" placeholder="Password"
                name="{{ form.password.html_name }}" autocomplete="current-password"
                required="" id="{{ form.password.id_for_label }}">
            </div>

            <div class="form-group">
                <button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
            </div>

            {% if form.errors %}
                {% for key,value in form.errors.items %}
                    {{ value }}
                {% endfor %}
            {% endif %}
        </form>
        <p class="mt-5 mb-3 text-body-secondary">© 2017-2023</p>
    </dev>
</div>
{% endblock BodyContent %}`
```

ログイン画面を見てみると以下のようになります。
なんとなくそれっぽい画面になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/3df255cc-1f61-b3f1-c729-6a1b754bab1a.png)

# 走り書き
forms.as_pの構成要素ということについて、色々気になったのでChatGPTに聞いてみました。

<h5>Q.django.contrib.auth.formsのsign-in関連のformを教えてください</h5>
<h5>A.
Djangoの django.contrib.auth.forms モジュールには、ユーザー認証（サインイン）に関連するフォームとして AuthenticationForm が提供されています。以下は AuthenticationForm の例です。</h5>

```
from django import forms
from django.contrib.auth.forms import AuthenticationForm

class LoginForm(AuthenticationForm):
    """
    A form for logging in a user. It uses Django's built-in AuthenticationForm,
    but you can customize it if needed.
    """
    class Meta:
        model = User  # Assuming you have a User model
        fields = ['username', 'password']
```

このfieldを見てみると、usernameとpasswordの2つが存在していることがわかりますね。
login.htmlのinput部分を以下に改めて記載していますが、★で囲われているusernameとかpasswordが上記usernameとかpasswordのことで、HTMLで入力されたものと合致するかどうか、ということを判断してくれているのかなと思いました。
```
input class="form-control" type="text" placeholder="Username" 
name="{{ form.★username★.html_name }}" autofocus="" autocapitalize="none"
autocomplete="{{ form.★username★.html_name }}" maxlength="150"
required="" id="{{ form.★username★.id_for_label }}

input class="form-control" type="password" placeholder="Password"
name="{{ form.★password★.html_name }}" autocomplete="current-password"
required="" id="{{ form.★password★.id_for_label }}
```