Help us understand the problem. What is going on with this article?

Python Django入門 (4)

Bootstrapの導入

Django で CRUD を作る説明の前に、Bootstrapの導入を行います。

我々エンジニアが作るHTMLは、デザイン的にも味気ないものとなってしまいます。

そこで、CSSフレームワークのBootstrapを使うことにします。

ドキュメントは英語ですが、実例が載っているので、やりたいことは見つかると思います。

Bootstrap

Bootstrap で必要な jQuery を含めて、以下からダウンロードします。

ダウンロードしたファイルを、mybook/cms/static/cms/ というディレクトリを作って、以下のように配置します。

*.map ファイルは、ブラウザーの開発者ツールで、JavaScriptのデバッグをしたり、HTMLの要素を見てCSSを検証したりする際に、元の行番号を出すためのものです。なくても動作はします。

bootstrap.bundle.min.js は、Popper.js を含む(バンドルしている)ものです。

mybook
└── cms
    └── static
        └── cms
            ├── css
            │   ├── bootstrap.min.css
            │   └── bootstrap.min.css.map
            └── js
                ├── bootstrap.bundle.min.js
                ├── bootstrap.bundle.min.js.map
                ├── jquery-3.4.1.min.js
                └── jquery-3.4.1.min.map

なぜ mybook/cms/static/cms/ なのかというと、

  • Django は static ディレクトリを、プロジェクト(mybook)配下の各アプリケーション(cms)の static ディレクトリを探します(mybook/cms/static)
  • 各アプリケーションの static ディレクトリは1つにまとめられて解釈されるため、識別のため自分のアプリケーション名(cms)を更に追加しておきます。(mybook/cms/static/cms)

これらは、後に出てくる template の探し方の仕組みとも似ていますが、アプリケーションを独立して再利用するためのものです。
今はわからなくてもかまいません。そういうものだと思って下さい。

django-bootstrap4

一覧系HTMLは、Bootstrapで必要なclassを手書きして、登録/修正のフォーム系HTMLは django-bootstrap4 という Python モジュールを使います。

インストールは、pip コマンドで行います。

python manage.py runserver で動いている時は、Ctrl + c で停止してから行ってください。
(env1) 仮想環境にいることを前提にしています。

残念ながら、今まで使っていた django-bootstrap-form は Bootstrap3 で更新が止まっていて、4には対応していないようなので、こちらに乗り換えました。

$ pip install django-bootstrap4

現時点では、1.1.1 が入りました。

$ pip freeze -l
asgiref==3.2.3
beautifulsoup4==4.8.2
Django==3.0.2
django-bootstrap4==1.1.1
pytz==2019.3
soupsieve==1.9.5
sqlparse==0.3.0

確認できたら、また python manage.py runserver で起動しておきましょう。

mybook/settings.py の INSTALLED_APPS に 'bootstrap4' を追加します。

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cms.apps.CmsConfig',   # cms アプリケーション
    'bootstrap4',           # django-bootstrap4
)

Django の CRUD

ビューをざっと作る

一覧、登録、修正、削除のファンクションが必要なので、`cms/views.py' にひな形を作ります。

登録、修正は編集としてひとまとめにしています。(book_idの指定がなければ登録、あれば修正)

コードスタイルとして、defの前も2行あけないと波線が出て、うるさいです。

from django.shortcuts import render
from django.http import HttpResponse


def book_list(request):
    """書籍の一覧"""
    return HttpResponse('書籍の一覧')


def book_edit(request, book_id=None):
    """書籍の編集"""
    return HttpResponse('書籍の編集')


def book_del(request, book_id):
    """書籍の削除"""
    return HttpResponse('書籍の削除')

URL スキームの設計

cms/urls.py というファイルは存在しないので、新しく作ります。

左側の cms の所で右クリックして、New > Python File で urls.py と指定します。

この中で、URLと、ビューのファンクションの紐付けを行います。

URLとファンクション名は一致させる必要はありません。このあたりは自由です。

from django.urls import path
from cms import views

app_name = 'cms'
urlpatterns = [
    # 書籍
    path('book/', views.book_list, name='book_list'),   # 一覧
    path('book/add/', views.book_edit, name='book_add'),  # 登録
    path('book/mod/<int:book_id>/', views.book_edit, name='book_mod'),  # 修正
    path('book/del/<int:book_id>/', views.book_del, name='book_del'),   # 削除
]

次に、 cms/urls.py をプロジェクト全体の mybook/urls.py の中でインクルードします。

from django.contrib import admin
from django.urls import path, include   # ←, includeを追加

urlpatterns = [
    path('cms/', include('cms.urls')),   # ←ここを追加
    path('admin/', admin.site.urls),
]

最終的にURLは以下のようになります。

ブラウザで確認してみて下さい。

http://127.0.0.1:8000/cms/book/
http://127.0.0.1:8000/cms/book/add/
http://127.0.0.1:8000/cms/book/mod/5/
http://127.0.0.1:8000/cms/book/del/7/

一覧のビュー

一覧を表示するビューを、きちんと書いてみます。

cms/views.pydef book_list を以下のように修正します。

from django.shortcuts import render
from django.http import HttpResponse

from cms.models import Book


def book_list(request):
    """書籍の一覧"""
    # return HttpResponse('書籍の一覧')
    books = Book.objects.all().order_by('id')
    return render(request,
                  'cms/book_list.html',     # 使用するテンプレート
                  {'books': books})         # テンプレートに渡すデータ

一覧のテンプレートを作る

mybook プロジェクトの cms アプリケーションで使う book_list.html というテンプレートを作成します。

位置は、以下のようになります。

mybook/cms/templates/cms/book_list.html

その前に、これの継承元となる base.html というテンプレートを作成しましょう。

左側の cms を右クリックして、New > Directory で templates を指定
template を右クリックして、New > Directory で cms を指定
cms を右クリックして、New > HTML File で base.html を指定、といった感じです。

mybook/cms/templates/cms/base.html

base.html の中身は、以下のようになります。

{% load i18n static %}
<!DOCTYPE html>{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="{% static 'cms/css/bootstrap.min.css' %}">
{% block extra_css %}{% endblock %}
<title>{% block title %}My books{% endblock %}</title>
</head>
<body>
  <div class="container">
    {% block content %}
      {{ content }}
    {% endblock %}
  </div>
<script src="{% static 'cms/js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'cms/js/bootstrap.bundle.min.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>

それでは、この base_html を継承して mybook/cms/templates/cms/book_list.html を作成します。

こちらは必要な所のみを書いていく形となります。

class に指定しているのは、Bootstrapで必要な class です。

{% extends "cms/base.html" %}

{% block title %}書籍の一覧{% endblock title %}

{% block content %}
    <h4 class="mt-4 border-bottom">書籍の一覧</h4>
    <a href="{% url 'cms:book_add' %}" class="btn btn-primary btn-sm my-3">追加</a>
    <table class="table table-striped table-bordered">
      <thead>
        <tr>
          <th scope="col">ID</th>
          <th scope="col">書籍名</th>
          <th scope="col">出版社</th>
          <th scope="col">ページ数</th>
          <th scope="col">操作</th>
        </tr>
      </thead>
      <tbody>
        {% for book in books %}
        <tr>
          <th scope="row">{{ book.id }}</th>
          <td>{{ book.name }}</td>
          <td>{{ book.publisher }}</td>
          <td>{{ book.page }}</td>
          <td>
            <a href="{% url 'cms:book_mod' book_id=book.id %}" class="btn btn-outline-primary btn-sm">修正</a>
            <a href="{% url 'cms:book_del' book_id=book.id %}" class="btn btn-outline-danger btn-sm">削除</a>
          </td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
{% endblock content %}

それではブラウザで確認してみましょう。

http://127.0.0.1:8000/cms/book/

django08.jpg

追加、修正のフォーム

cms/forms.py というファイルを作って、以下のように記述します。
ここでは、cms/models.py の Book モデルを追加、修正するための元となるフォームを作成します。

from django.forms import ModelForm
from cms.models import Book


class BookForm(ModelForm):
    """書籍のフォーム"""
    class Meta:
        model = Book
        fields = ('name', 'publisher', 'page', )

追加、修正のビュー

cms/views.pydef book_edit を以下のように修正します。

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse

from cms.models import Book
from cms.forms import BookForm

  :

def book_edit(request, book_id=None):
    """書籍の編集"""
    # return HttpResponse('書籍の編集')
    if book_id:   # book_id が指定されている (修正時)
        book = get_object_or_404(Book, pk=book_id)
    else:         # book_id が指定されていない (追加時)
        book = Book()

    if request.method == 'POST':
        form = BookForm(request.POST, instance=book)  # POST された request データからフォームを作成
        if form.is_valid():    # フォームのバリデーション
            book = form.save(commit=False)
            book.save()
            return redirect('cms:book_list')
    else:    # GET の時
        form = BookForm(instance=book)  # book インスタンスからフォームを作成

    return render(request, 'cms/book_edit.html', dict(form=form, book_id=book_id))

追加、修正のテンプレート

mybook/templates/base_html を継承して mybook/cms/templates/cms/book_edit.html を作成します。

{% extends "cms/base.html" %}
{% load bootstrap4 %}

{% block title %}書籍の編集{% endblock title %}

{% block content %}
    <h4 class="mt-4 mb-5 border-bottom">書籍の編集</h4>
    {% if book_id %}
    <form action="{% url 'cms:book_mod' book_id=book_id %}" method="post">
    {% else %}
    <form action="{% url 'cms:book_add' %}" method="post">
    {% endif %}
      {% csrf_token %}
      {% bootstrap_form form layout='horizontal' %}
      <div class="form-group row">
        <div class="offset-md-3 col-md-9">
          <button type="submit" class="btn btn-primary">送信</button>
        </div>
      </div>
    </form>
    <a href="{% url 'cms:book_list' %}" class="btn btn-secondary btn-sm">戻る</a>
{% endblock content %}

cms/forms.py の BookForm で定義した項目を、cms/views.py で form という変数でテンプレートに渡し、{% bootstrap_form form layout='horizontal' %} と書いている form の部分で、フォームの内容がHTMLで展開されます。

ここではさらに、bootstrap_formタグを使うことにより、Bootstrapの書式に変換しています。

書籍の登録ページは、以下のようになります。

django09.jpg

削除のビュー

cms/views.pydef book_del を以下のように修正します。

def book_del(request, book_id):
    """書籍の削除"""
    # return HttpResponse('書籍の削除')
    book = get_object_or_404(Book, pk=book_id)
    book.delete()
    return redirect('cms:book_list')

それでは、書籍の一覧ページから「削除」ボタンを押して、書籍を削除してみてください。

django08.jpg

いきなり消していますが、本当は Bootstrap の モーダルダイアログを出して、確認メッセージを出した方がいいでしょう。
これは後に取り組むこととします。

Python Django入門 (5) に続きます。

kaki_k
jsl
株式会社日本システム技研は、1976年に設立した長野に本社を構えるIT企業です。主にPython/Djangoの使用したWebアプリケーションの受託開発とプロダクト開発を行なっています。エンジニア向けコミュニティGEEKLAB.NAGANO( http://geeklab-nagano.com ) の運営をしています。
https://jsl.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away