自分だけのクイズ作成 - Quipha公開中

【Django4】CRUDアプリをBootstrapでデザイン調整

Django
スポンサーリンク

はじめに

Djangoで作成したアプリケーションについて、Bootstrapでデザインを調整する方法を解説します。

今回は、前回作成した以下のCRUDアプリケーションのデザインを、Bootstrapを利用して調整します。

他にも私のブログで、Djangoについて解説している記事がありますのでご覧ください。

使用するバージョン
  • Windows 11 / 10
  • macOS Monterey (M1)
  • Python 3.10.2
  • Django 4.0.2
  • Bootstrap 5.0.2

WindowsでもMacでも同様に作成できます。

余談ですが、Laravel向けに解説した記事もございますので参考にしてください。

スポンサーリンク

VS Codeインストール

VS Codeのインストール方法は、以下の記事にまとめましたのでご覧ください。

VS Codeのオススメ設定や拡張機能などは、以下の記事にまとめました。

共通テンプレートの作成

まずは、共通部分は共通テンプレートにしましょう
これにより、毎回ヘッダーやフッターなどを記述しなくて済みます。

crud/templates/crud/base.html
<html>

<head>
    <meta charset='utf-8' />
</head>

<body>
    {% block content %}{% endblock %}
</body>

</html>

画面の修正を行う例です。
以下のように共通テンプレートを読み込み、本文を記述します。

{% extends 'crud/base.html' %}

{% block content %}

本文

{% endblock %}

一覧画面は以下のように修正します。

crud/templates/crud/product_list.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>商品一覧</h1>

<a href="{% url 'crud:new' %}">新規登録</a>

<table border="1">
    <thead>
        <tr>
            <td>商品名</td>
            <td>価格</td>
            <td></td>
        </tr>
    </thead>
    {% for product in object_list %}
    <tr>
        <td>{{ product.name }}</td>
        <td>{{ product.price }} 円</td>
        <td>
            <a href="{% url 'crud:edit' pk=product.pk %}">編集</a>
            <a href="{% url 'crud:delete' pk=product.pk %}">削除</a>
        </td>
    </tr>
    {% endfor %}
</table>

{# ページの表示 #}
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }} ページ<br>

{# 前へ #}
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; 先頭</a>
<a href="?page={{ page_obj.previous_page_number }}">前へ</a>
{% endif %}

{# 次へ #}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">次へ</a>
<a href="?page={{ page_obj.paginator.num_pages }}">最後 &raquo;</a>
{% endif %}

{% endblock %}

この時点で、画面を表示してみましょう。
エラーが表示されなければOKです。

他の画面も同様に修正します。

crud/templates/crud/product_form.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>新規登録</h1>

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <a href="{% url 'crud:list' %}">戻る</a>
    <input type="submit" value="登録">
</form>

{% endblock %}
crud/templates/crud/product_update_form.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>編集</h1>

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <a href="{% url 'crud:list' %}">戻る</a>
    <input type="submit" value="編集">
</form>

{% endblock %}
crud/templates/crud/product_confirm_delete.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>削除</h1>

<form method="post">
    {% csrf_token %}

    以下の商品を削除してもよろしいですか?<br>
    {{ object }}<br>

    {{ form }}
    <a href="{% url 'crud:list' %}">戻る</a>
    <input type="submit" value="削除">
</form>

{% endblock %}

Bootstrap導入

Bootstrapを導入します。
CDNを利用しますが、最新である5系を使用します。

はじめに
世界で最も人気のあるフレームワーク Bootstrap で始めましょう。CDN とテンプレートを使ってモバイルファーストなサイトを構築できます。

共通テンプレートを修正します。
これでBootstrapの導入ができました。

crud/templates/crud/base.html
<!doctype html>
<html>

<head>
    <meta charset='utf-8' />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>

<body>
    {% block content %}{% endblock %}

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
</body>

</html>

今回はナビゲーションバーを設置してみました。

crud/templates/crud/base.html
<!doctype html>
<html>

<head>
    <meta charset='utf-8' />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <style>
        body {
            padding-top: 70px;
        }
    </style>
</head>

<body>

    <header>
        <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
            <div class="container-fluid">
                <a class="navbar-brand" href="#">CRUD</a>
            </div>
        </nav>
    </header>

    <div class="container">
        {% block content %}{% endblock %}
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>
</body>

</html>

このような画面になりました。

スポンサーリンク

django-widget-tweaks

新規登録画面など、現状は以下の一行でフォームに関するHTMLタグを出力しています。

{{ form.as_p }}

この場合、各タグにCSSクラスを追加したい場合に不便です。

実際にデザインを調整する上で、クラスの指定やタグの追加などを行いますので、今回は「django-widget-tweaks」を使用します。

今回はVS Codeを使い、Dockerコンテナで作業していますので、以下のファイルを修正します。

一番最後の行を追加します。(django-widget-tweaks)

.devcontainer/Dockerfile
FROM python:3
ENV PYTHONUNBUFFERED 1
RUN pip install django
RUN pip install mysqlclient

RUN pip install django-widget-tweaks

F1キーをクリックし、コンテナをリビルドします。(Remote-Containers: Rebuild Container)

settings.pyを修正し、django-widget-tweaksを読み込みます。

djangoCrudApp/settings.py
INSTALLED_APPS = [
...
    'widget_tweaks',
    'crud',
]

使い方は以下のような感じです。

{% load widget_tweaks %}

<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}

一覧画面の修正

一覧画面のテーブルやページネーションの調整を行います。

crud/templates/crud/product_list.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>商品一覧</h1>

<a class="btn btn-light" href="{% url 'crud:new' %}">新規登録</a>

<table class="table">
    <thead>
        <tr>
            <td>商品名</td>
            <td>価格</td>
            <td></td>
        </tr>
    </thead>
    {% for product in object_list %}
    <tr>
        <td>{{ product.name }}</td>
        <td>{{ product.price }} 円</td>
        <td>
            <a class="btn btn-primary" href="{% url 'crud:edit' pk=product.pk %}">編集</a>
            <a class="btn btn-danger" href="{% url 'crud:delete' pk=product.pk %}">削除</a>
        </td>
    </tr>
    {% endfor %}
</table>

<div class="d-flex justify-content-between">
    <div>
        {# ページの表示 #}
        {{ page_obj.number }} / {{ page_obj.paginator.num_pages }} ページ<br>
    </div>
    <div class="btn-group">
        {# 前へ #}
        {% if page_obj.has_previous %}
        <a class="btn btn-outline-secondary" href="?page=1">&laquo; 先頭</a>
        <a class="btn btn-outline-secondary" href="?page={{ page_obj.previous_page_number }}">前へ</a>
        {% else %}
        <a class="btn btn-outline-secondary disabled">&laquo; 先頭</a>
        <a class="btn btn-outline-secondary disabled">前へ</a>
        {% endif %}

        {# 次へ #}
        {% if page_obj.has_next %}
        <a class="btn btn-outline-secondary" href="?page={{ page_obj.next_page_number }}">次へ</a>
        <a class="btn btn-outline-secondary" href="?page={{ page_obj.paginator.num_pages }}">最後 &raquo;</a>
        {% else %}
        <a class="btn btn-outline-secondary disabled">次へ</a>
        <a class="btn btn-outline-secondary disabled">最後 &raquo;</a>
        {% endif %}
    </div>
</div>

{% endblock %}

以下のURLにアクセスし確認しましょう。

http://localhost:8000/crud/

一覧画面が表示されました。
新規登録ボタンや編集、削除、そしてページネーションのデザインが調整されました。

新規登録画面の修正

新規登録画面を修正します。
formについては、django-widget-tweaksを使用してクラスを指定し調整します。

crud/templates/crud/product_form.html
{% extends 'crud/base.html' %}

{% load widget_tweaks %}

{% block content %}

<h1>新規登録</h1>

<form method="post">
    {% csrf_token %}

    <div class="mb-3">
        {{ form.name|add_label_class:"form-label" }}
        {{ form.name|add_class:"form-control" }}

        {{ form.price|add_label_class:"form-label" }}
        {{ form.price|add_class:"form-control" }}
    </div>

    <a class="btn btn-secondary" href="{% url 'crud:list' %}">戻る</a>
    <input class="btn btn-primary" type="submit" value="登録">
</form>

{% endblock %}

一度、以下のURLにアクセスし確認しましょう。

http://localhost:8000/crud/new/

新規登録画面が表示されました。

商品名と価格についてはそれぞれ、「Name」と「Price」と表示されていますので変更します。
モデルを以下のように修正します。(verbose_nameを指定)

crud/models.py
from django.db import models
from django.urls import reverse

# Create your models here.


class Product(models.Model):

    name = models.CharField(max_length=200, verbose_name="商品名")
    price = models.IntegerField(verbose_name="価格")

    def __str__(self):
        return self.name

    # 新規登録・編集完了後のリダイレクト先
    def get_absolute_url(self):
        return reverse('crud:list')

再度画面を表示してみると、今度は日本語で表示されました。

編集画面の修正

編集画面を修正します。新規登録画面と同じような要領です。

crud/templates/crud/product_update_form.html
{% extends 'crud/base.html' %}

{% load widget_tweaks %}

{% block content %}

<h1>編集</h1>

<form method="post">
    {% csrf_token %}

    <div class="mb-3">
        {{ form.name|add_label_class:"form-label" }}
        {{ form.name|add_class:"form-control" }}

        {{ form.price|add_label_class:"form-label" }}
        {{ form.price|add_class:"form-control" }}
    </div>

    <a class="btn btn-secondary" href="{% url 'crud:list' %}">戻る</a>
    <input class="btn btn-primary" type="submit" value="編集">
</form>

{% endblock %}

一覧画面の編集ボタンをクリックして確認します。
編集画面が表示されました。

スポンサーリンク

削除画面の修正

削除画面を修正します。

crud/templates/crud/product_confirm_delete.html
{% extends 'crud/base.html' %}

{% block content %}

<h1>削除</h1>

<form method="post">
    {% csrf_token %}

    <div class="alert alert-danger" role="alert">
        以下の商品を削除してもよろしいですか?
    </div>

    <div class="alert alert-light" role="alert">
        {{ object }}
    </div>

    {{ form }}
    <a class="btn btn-secondary" href="{% url 'crud:list' %}">戻る</a>
    <input class="btn btn-danger" type="submit" value="削除">
</form>

{% endblock %}

一覧画面の削除ボタンをクリックして確認します。
削除画面が表示されました。

完成ソース

今回作成したプロジェクトのソース全容は、以下のリポジトリをご覧ください。

GitHub - chigusa-web/djangoCrudApp
Contribute to chigusa-web/djangoCrudApp development by creating an account on GitHub.

その他

外部サーバーへ公開

作成したアプリは公開して使ってもらいましょう!
Djangoアプリケーションを外部公開する方法をまとめました。

脆弱性対策

脆弱性を抱えたアプリケーションの場合、攻撃を受ける可能性があり大変危険です。
作成したアプリケーションは、脆弱性対策も意識しましょう。

GitHubと連携

GitHubと連携する方法を解説しました。
プロジェクトの管理はGitHubを活用しましょう。

さいごに

今回はBootstrapを利用して、デザインを調整しました。
レスポンシブデザインにも対応でき、便利です。

ぜひ活用しましょう。

他にも私のブログで、Djangoについて解説している記事がありますのでご覧ください。

\オススメ/

コメント

タイトルとURLをコピーしました