【Django3.1】Djangoで作成したアプリケーションにBootstrap5-beta1を導入し、見た目を整える。
なお本記事のソースコードはgithub上に公開しています。 djangoでbootstrapの導入方法いくつかある。 今回は1のやり方で導入を行う。 staticディレクトリをプロジェクトディレクトリ直下に以下の用に作成。 staticファイルの参照先を設定。 confug/settings.py staticファイルの扱い方の詳細は以下を参照。
- 公式document
Managing static files (e.g. images, JavaScript, CSS) | Django documentation | Django Bootstrap5のサイトからbootstrapモジュールをダウンロードする。 ダウンロードしたファイルのうち以下のモジュールを以下のように配置する。
どのテンプレートにも共通で書かなければ行けない場合、Djangoにはベーステンプレートを作成することで、共通かでる。
templates/base.html bodyタグに注目すると、 以下のようなイメージ。 やって見たほうが早いのでlist.htmlを以下の用に修正する。 templates/list.html この状態でrunserverして
http://127.0.0.1:8000/samplecrud/list/
にアクセスすると、以下の用に変化したことがわかる。 list.htmlと同じ用に他のテンプレートも編集しておく。 templates/create.html templates/delete.html templates/update.html ※ここからはBootstrapの使い方になるのでDjangoとは少し遠い世界になる。 見た目といえばNavbar。 templates/base.html listとcreateページをリンクをNavbarに追加した。 レスポンシブデザインになっている。 余談だが、bootstrap4→5で.float-left、.float-rightは.float-startと.float-endに変更になっていた。 templates/list.html だいぶwebサイトっぽくなってきましたね。 templates/list.html だいぶwebサイトっぽくなってきましたね。(2回目) 以上、Djangoで作成したアプリケーションにBootstrap5-beta1を導入し、見た目を整えるでした。 まず、bootstrapのformを導入する前の templates/create.html ほぼ 今回のやり方でbootstrapを適応する場合はこのformを展開してやる必要がある。 方法としてはformクラスを個別に展開します。 上記を参照して個別展開すると以下のように表現できる。 inputタグや、textareaタグは ここまでくればbootstrapの世界に持ってこれるので以下の用にする。 この状態で
http://127.0.0.1:8000/samplecrud/create/
にアクセスすると以下の用に表示される。 これまたちょっとwebサイトっぽくなりましたね。 同じ用にupdateも更新する。
templates/update.html updateベージは以下の用に表示される。 削除ページは戻るButtonを追加 daleteベージは以下の用に表示される。 以上、Djangoで作成したアプリケーションにBootstrap5-beta1を導入し、見た目を整える。でした。本記事で行うこと
n-guitar.hatenablog.com
GitHub - n-guitar/django-samples at feature/bootstrap5/sample.generic-viewモチベーション
環境
$ sw_vers
ProductName: macOS
ProductVersion: 11.1
BuildVersion: 20C69
$ python -V
Python 3.8.0
Bootstrap5導入
参考:https://github.com/zostera/django-bootstrap4
※当然Bootstrap5は2021/1/11時点でまだbataなのでdjango-bootstrap5は存在しない。staticディレクトリの作成と設定
STATIC_URL = '/static/'
STATICFILES_DIRS = (
BASE_DIR / 'static',
)
Bootstrap5のダウンロードとstaticファイルの配置
Download · Bootstrap v5.0
baseテンプレートの作成
templateディレクトリ配下に以下の様にbase.htmlを作成し、先程格納したbootstrapモジュールのリンクを記述する。{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
<title>sample app</title>
</head>
<header>
<h1>base.htmlのheader</h1>
</header>
<body>
{% block Content %}
<h1>base.htmlのbody</h1>
{% endblock Content %}
</body>
<footer>
<h1>base.htmlのfooter</h1>
</footer>
</html>
{% block Content %} {% endblock Content %}
と記載している。Content
というのは任意の名前で構わない。
このblockで挟まれた部分を他のテンプレートで上書きできる。
blockは任意の複数書くことも出来る。
{% extends 'base.html' %}
{% block Content %}
<h1>list Page</h1>
<ul>
{% for item in object_list %}
<li>
{{ item.title }}: {{ item.memo }}
<a href="{% url 'update' item.pk %}">編集する</a>
<a href="{% url 'delete' item.pk %}">削除する</a>
</li>
{% endfor %}
</ul>
{% endblock Content %}
{% extends 'base.html' %}
でbase.htmlを継承し、上書きしたい部分を{% block Content %} {% endblock Content %}
で囲う。
↓
{% extends 'base.html' %}
{% block Content %}
<h1>Create Page</h1>
<form action="" method="POST"> {% csrf_token %}
{{ form.as_p}}
<input type="submit" value="作成する">
</form>
{% endblock Content %}
{% extends 'base.html' %}
{% block Content %}
<h1>Delete Page</h1>
<form action="" method="POST"> {% csrf_token %}
<p>
{{ object.title }}{{ object.memo }}
を本当に削除しますか?
</p>
<input type="submit" value="削除する">
</form>
{% endblock Content %}
{% extends 'base.html' %}
{% block Content %}
<h1>Update Page</h1>
<form action="" method="POST"> {% csrf_token %}
{{ form.as_p}}
<input type="submit" value="更新する">
</form>
{% endblock Content %}
Bootstrapで見た目を整える。
Navbarの導入
以下のsampleに習って作成する。
Navbar · Bootstrap v5.0{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
<script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
<title>sample app</title>
</head>
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">Todo Sample App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link " aria-current="page" href="{% url 'list' %}">List</a>
<a class="nav-link" href="{% url 'create' %}">Create</a>
</div>
</div>
</div>
</nav>
</header>
<body>
{% block Content %}
{% endblock Content %}
</body>
<footer>
<div class="fixed-bottom">
<p class="float-end m-2">create by haku-mai</p>
</div>
</footer>
</html>
以下の用になる。
jumbotronとButtonの導入 (bootstrap5からjumbotronが削除されたので他のユーティリティを利用)
{% extends 'base.html' %}
{% block Content %}
<div class="bg-light p-3 p-sm-5 mb-4 border-top">
<div class="container">
<h1 class="display-4">List Page</h1>
<hr class="my-4">
<p>djangoでいっぱい遊びましょう</p>
<a class="btn btn-primary btn-lg" href="{% url 'create' %}" role="button">ToDoを作成する</a>
</div>
</div>
<ul>
{% for item in object_list %}
<li>
{{ item.title }}: {{ item.memo }}
<a href="{% url 'update' item.pk %}">編集する</a>
<a href="{% url 'delete' item.pk %}">削除する</a>
</li>
{% endfor %}
</ul>
{% endblock Content %}
tableの導入
{% extends 'base.html' %}
{% block Content %}
<div class="bg-light p-3 p-sm-5 mb-4 border-top">
<div class="container">
<h1 class="display-4">List Page</h1>
<hr class="my-4">
<p>djangoでいっぱい遊びましょう</p>
<a class="btn btn-primary btn-lg" href="{% url 'create' %}" role="button">ToDoを作成する</a>
</div>
</div>
<ul>
<div class="container">
<table class="table caption-top">
<caption>Todo List</caption>
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Memo</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for item in object_list %}
<tr>
<td>{{ item.title }}</td>
<td>{{ item.memo }}</td>
<td><a class="btn btn-secondary btn-sm" href="{% url 'update' item.pk %}">編集する</a></td>
<td><a class="btn btn-danger btn-sm" href="{% url 'delete' item.pk %}">削除する</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock Content %}
のを {% for item in object_list %} {% endfor %}
で囲うのがポイント。
form導入について
create.html
をもう一度見てみると、generic viewのCreateViewを使いDjangoの機能を最大限利用している関係で、
form文をほとんど書かなくても実装出来ている。{% extends 'base.html' %}
{% block Content %}
<h1>Create Page</h1>
<form action="" method="POST"> {% csrf_token %}
{{ form.as_p}}
<input type="submit" value="作成する">
</form>
{% endblock Content %}
{{ form.as_p}}
しか書いていない。
実際にレンダリングされる時、{{ form.as_p}}
は以下の用に展開されている。<p><label for="id_title">Title:</label> <input type="text" name="title" maxlength="100" required="" id="id_title"></p>
<p><label for="id_memo">Memo:</label> <textarea name="memo" cols="40" rows="10" maxlength="255" required="" id="id_memo"></textarea></p>
formクラスは非常に簡単であるが、見た目を返上するにはやや面倒であり、直接form用のhtmlを記載する方法もあるが、
formクラスにはエラー処理やバリデーションの機能などを兼ね備えているため、できるだけ利用してbootstrapを反映させて見ます。
formクラスが持つフィールド詳細については以下を参照
- 公式document
Working with forms | Django documentation | Django{% extends 'base.html' %}
{% block Content %}
<h1>Create Page</h1>
<form action="" method="POST"> {% csrf_token %}
<p>
<label for="{{ form.title.id_for_label }}">{{ form.title.label }}</label>
<input type="text" name="{{ form.title.html_name }}" maxlength="100" required="" id="{{ form.title.id_for_label }}">
{% for error in form.title.errors %}
{{error}}
{% endfor %}
</p>
<p>
<label for="{{ form.memo.id_for_label }}">{{ form.memo.label }}</label>
<textarea name="{{ form.memo.html_name }}" cols="40" rows="10" maxlength="255" required="" id="{{ form.memo.id_for_label }}"></textarea>
{% for error in form.memo.errors %}
{{error}}
{% endfor %}
</p>
<input type="submit" value="作成する">
</form>
{% endblock Content %}
{{ form.title }}
と{{ form.memo }}
で置き換えることが出来るが、bootstrapで加工したいため、利用しない。
この状態で
http://127.0.0.1:8000/samplecrud/create/
にアクセスすると以下の用に表示される。
{% extends 'base.html' %}
{% block Content %}
<div class="container">
<form action="" method="POST"> {% csrf_token %}
<legend>Todoを作成</legend>
<div class="mb-3">
<label class="form-label" for="{{ form.title.id_for_label }}">{{ form.title.label }}</label>
<input class="form-control" type="text" name="{{ form.title.html_name }}" maxlength="100" required="" id="{{ form.title.id_for_label }}">
{% for error in form.title.errors %}
{{error}}
{% endfor %}
</div>
<div class="mb-3">
<label class="form-label" for="{{ form.memo.id_for_label }}">{{ form.memo.label }}</label>
<textarea class="form-control" name="{{ form.memo.html_name }}" cols="40" rows="10" maxlength="255" required="" id="{{ form.memo.id_for_label }}"></textarea>
{% for error in form.memo.errors %}
{{error}}
{% endfor %}
</div>
<input class="btn btn-primary" type="submit" value="作成する">
</form>
</div>
{% endblock Content %}
{% extends 'base.html' %}
{% block Content %}
<div class="container">
<form action="" method="POST"> {% csrf_token %}
<legend>Todoを更新</legend>
<div class="mb-3">
<label class="form-label" for="{{ form.title.id_for_label }}">{{ form.title.label }}</label>
<input class="form-control" type="text" name="{{ form.title.html_name }}" value="{{ form.title.value }}" maxlength="100" required="" id="{{ form.title.id_for_label }}">
{% for error in form.title.errors %}
{{error}}
{% endfor %}
</div>
<div class="mb-3">
<label class="form-label" for="{{ form.memo.id_for_label }}">{{ form.memo.label }}</label>
<textarea class="form-control" name="{{ form.memo.html_name }}" cols="40" rows="10" maxlength="255" required="" id="{{ form.memo.id_for_label }}">{{ form.memo.value }}</textarea>
{% for error in form.memo.errors %}
{{error}}
{% endfor %}
</div>
<input class="btn btn-primary" type="submit" value="更新する">
</form>
</div>
{% endblock Content %}
{% extends 'base.html' %}
{% block Content %}
<div class="container">
<h1 class="display-6">Todoを削除確認</h1>
<form action="" method="POST"> {% csrf_token %}
<p>
{{ object.title }}{{ object.memo }}
を本当に削除しますか?
</p>
<input class="btn btn-danger" type="submit" value="削除する">
<a class="btn btn-secondary" href="{% url 'list' %}">戻る</a>
</form>
</div>
{% endblock Content %}