Edited at

Python Django入門 (4)

More than 1 year has passed since last update.


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.3.1.min.js
        └── jquery-3.3.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

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

$ pip freeze -l

Django==2.0.1
django-bootstrap4==0.0.5

確認できたら、また 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',
'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.3.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) に続きます。