18c. Django入門3(Djangoのテンプレートとフォームを理解)

テンプレートの共通化

共通部分をまとめると、Webデザインの作成やメンテナンスを効率よく進めることができます。

base.htmlを作成

<!-- myapp/bbs/templates/bbs/base.html -->
< !DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'/>
        <title>paiza bbs</title>
        <style>body {padding: 80px;}</style>
    </head>
    <body>
        {% block content %}
        {% endblock %}
    </body>
</html>

index.htmlの共通部分を分離

<!-- myapp/bbs/templates/bbs/index.html -->
{% extends './base.html' %}		<!-- 共通テンプレートを指定 -->
{% block content %}
    <h1>paiza bbs</h1>
    <p>{{ message }}</p>
    {% for article in articles %}
        <p>
            {{ article.content }},{{ article.user_name }},
            <a href='{% url "bbs:detail" article.id %}'>詳細</a>,
            <a href='{% url "bbs:delete" article.id %}'>削除</a>
        </p>
    {% endfor %}

    <p>
        <a href='{% url "bbs:create" %}'>新規</a>
    </p>
{% endblock %}

detail.htmlの共通部分を分離

<!-- myapp/bbs/templates/bbs/detail.html -->
{% extends './base.html' %}		<!-- 共通テンプレートを指定 -->
{% block content %}
    <h1>paiza bbs</h1>
    <p>{{ message }}</p>
    <p>{{ article.content }}, {{ article.user_name }}</p>
    <p><a href='{% url "bbs:index" %}'>一覧</a></p>
{% endblock %}

掲示板にBootstrapを適用

CSSフレームワークのBootstrapを利用すると、Webアプリケーションのデザインを簡単に設定できます。

Bootstrapを適用する

<!-- myapp/bbs/templates/bbs/base.html -->
< !DOCTYPE html>
<html>
    <head>
        <meta charset='utf-8'/>
        <!-- metaタグでviewportを指定 -->
        <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'/>
        <title>paiza bbs</title>
        <!-- bootstrapを読み込み -->
        <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'/>
        <style>body {padding-top: 80px;}</style>
    </head>
    <body>
        <!-- ナビゲーションバーを追加 -->
        <nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'>
            <a class='navbar-brand' href='{% url "bbs:index" %}'>paiza bbs</a>
        </nav>
        <div class='container'>		<!-- bootstrapがデザインしてくれる -->
            {% block content %}
            {% endblock %}
        </div>
    </body>
</html>

※ bootstrap4をダウンロードして使用する場合は以下を参照
Bootstrap4の導入方法と基本を徹底解説!
Django staticファイル まとめ - Qiita

Bootstrapでページの見栄えを整える

Bootstrapには、テーブルタグやボタンに見栄えの良いスタイルが用意してあるので、これを利用していきます。

一覧ページをテーブルとボタンに変更

<!-- myapp/bbs/templates/bbs/index.html -->
{% extends './base.html' %}
{% block content %}
    <h1>paiza bbs</h1>
    <p>{{ message }}</p>

    <!-- stripedで1行毎に色を付ける -->
    <table class='table table-striped table-hover'>
        {% for article in articles %}
            <tr>
                <td>{{ article.content }}</td>
                <td>{{ article.user_name }}</td>
                <td>
                    <!-- リンクをボタンに変更 -->
                    <a href='{% url "bbs:detail" article.id %}' class='btn btn-outline-primary'>詳細</a>
                    <a href='{% url "bbs:delete" article.id %}' class='btn btn-outline-secondary'>削除</a>
                </td>
            </tr>
        {% endfor %}
    </table>

    <div>
        <a href='{% url "bbs:create" %}' class='btn btn-outline-primary'>新規</a>
    </div>
{% endblock %}

詳細ページのボタンを変更

<!-- myapp/bbs/templates/bbs/detail.html -->
{% extends './base.html' %}
{% block content %}
    <h1>paiza bbs</h1>
    <p>{{ message }}</p>
    <p>{{ article.content }}, {{ article.user_name }}</p>
    <p>
        <a href='{% url "bbs:index" %}' class='btn btn-outline-primary'>一覧</a>
    </p>
{% endblock %}

検索フォームを設置する

Djangoでフォームを使うための基本的な操作を理解していきます。

フォームを利用する流れ

  1. forms.py: フォームのクラスで、データ形式を定義する
  2. views.py: フォームから受け取ったデータを処理して、フォームオブジェクトをテンプレートに渡す
  3. index.html: フォームを表示する

forms.pyを記述

# myapp/bbs/forms.py
from django import forms

class SearchForm(forms.Form):			# SearchFormクラスを定義
        # データ形式が文字列のkeyword変数を定義
        keyword = forms.CharField(label='検索', max_length=100)

views.pyにフォームオブジェクトを追加

# myapp/bbs/views.py
from .forms import SearchForm

def index(request):
    searchForm = SearchForm(request.GET)
    # フォームから正常なデータを受信した場合
    if searchForm.is_valid():
        keyword = searchForm.cleaned_data['keyword']	# keyword変数にその値を代入
        # キーワードに該当する投稿をfilterメソッドで取り出す
        articles = Article.objects.filter(content__contains=keyword)
    else:
        searchForm = SearchForm()
        articles = Article.objects.all()

    context = {
        'message': 'Hello Django',
        'articles': articles,
        'searchForm': searchForm,
    }
    return render(request, 'bbs/index.html', context)

テンプレートにフォームを追加

<!-- myapp/bbs/templates/bbs/index.html -->
{% extends './base.html' %}
{% block content %}
    <h1>paiza bbs</h1>
    <p>{{ message }}</p>

	{% if searchForm %}
	    <!-- OKボタンを押すと、getメソッドをindex関数を呼び出す -->
	    <form action='{% url "bbs:index" %}' method='get'>
	        <div class='form-group'>
	            {{ searchForm }}		<!-- ビューから受け取ったsearchFormを表示 -->
	            <input type="submit" class="btn btn-outline-primary" value="OK" />
	            <a href="{% url 'bbs:index' %}" class="btn btn-outline-secondary">クリア</a>
	        </div>
	    </form>
	{% endif %}

掲示板のルーティングを設計する

投稿機能を作成する為のルーティングを設計していきます。

掲示板アプリケーションのルーティング設定

URL関数呼び出す機能
bbs/index()投稿一覧
bbs/(id)detail()個別投稿
bbs/newnew()新規作成
bbs/createcreate()新規投稿
bbs/(id)/editedit()編集
bbs/(id)/updateupdate()更新
bbs/(id)/deletedelete()削除

urls.pyにルートを追加

# myapp/bbs/urls.py
from django.urls import path
from . import views

app_name = 'bbs'

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:id>', views.detail, name='detail'),
    path('new', views.new, name='new'),						# 追加
    path('create', views.create, name='create'),
    path('</int:id><int:id>/edit', views.edit, name='edit'),			# 追加
    path('</int:id><int:id>/update', views.update, name='update'),	# 追加
    path('</int:id><int:id>/delete', views.delete, name='delete'),
]

views.pyに関数を追加

# myapp/bbs/views.py
def new(request):
    return HttpResponse('this is new.')

def edit(request, id):
    return HttpResponse('this is edit ' + str(id))

def update(request, id):
    return HttpResponse('this is update ' + str(id))

以下にアクセスして、想定通りの出力があることを確認します。
http://localhost:8000/bbs/new
http://localhost:8000/bbs/1/edit
http://localhost:8000/bbs/1/update

ライブラリのインストール
データベースを更新するフォームでBootstrapを使用できるように、Django Bootstrap4ライブラリをインストールします。

$ pip install django-bootstrap4
$ pip list				# 確認

setting.pyを修正

# settings.py
INSTALLED_APPS = [
    'bbs.apps.BbsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap4',
]

新規投稿フォームを作成する

forms.pyで投稿フォームのデータ形式を定義して、views.pyでindex関数にコードを記述していきます。

forms.pyに新規投稿フォームを定義

# myapp/bbs/forms.py
from django import forms
from .models import Article

# 投稿用フォームの定義
class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ('content', 'user_name')

new関数を記述

# myapp/bbs/views.py
from .forms import ArticleForm

def new(request):
    # ①ArticleFormオブジェクトを用意
    articleForm = ArticleForm()

    context = {
        'message': 'New Article',
        'articleForm': articleForm,		# ②コンテキストに追加
    }

    # ③テンプレートの呼び出し
    return render(request, 'bbs/new.html', context)

投稿の保存機能を完成させる

前回作成した、新規投稿を格納するcreate関数と投稿フォームを組み合わせていきます。

new.htmlの作成

<!-- myapp/bbs/templates/bbs/new.html -->
{% extends './base.html' %}
{% load bootstrap4 %}

{% block content %}
    <h1>{{ message }}</h1>

    <!-- 作成するボタンをクリックすると、createルートをpostメソッドで呼び出す -->
    <form action='{% url "bbs:create" %}' method='post' class='form'>
        {% csrf_token %}		<!-- セキュリティの為の記述 -->
        <!-- bootstrapを利用して、articleの各フォームを表示 -->
        {% bootstrap_form articleForm layout='horizontal' %}
        <button type='submit' class='btn btn-outline-primary'>作成する</button>
        <a href='{% url "bbs:index" %}' class='btn btn-outline-secondary'>戻る</a>
    </form>
{% endblock %}

create関数を修正

# myapp/bbs/views.py
def create(request):
    if request.method == 'POST':
        articleForm = ArticleForm(request.POST)		# POSTメソッドならデータを取り出す
        if articleForm.is_valid():
            article = articleForm.save()			# 正常なデータならsaveメソッドで保存

    context = {
        'message': 'Create article ' + str(article.id),
        'article': article,
    }
    return render(request, 'bbs/detail.html', context)

index.htmlの一覧から/newを呼び出す

<!-- myapp/bbs/templates/bbs/index.html -->
<div>
    <a href='{% url "bbs:new" %}' class='btn btn-outline-primary'>新規</a>
</div>

編集フォームの追加

既存の投稿を修正できる編集フォームを作成していきます。

views.pyに「/edit」を追加

# myapp/bbs/views.py
def edit(request, id):
    article = get_object_or_404(Article, pk=id)
    articleForm = ArticleForm(instance=article)		# ArticleFormオブジェクトを生成

    context = {
        'message': 'Edit Article' + str(id),
        'article': article,
        'articleForm': articleForm,
    }
    return render(request, 'bbs/edit.html', context)	# edit.htmlテンプレートを呼び出す

edit.htmlを追加

<!-- myapp/bbs/templates/bbs/edit.html -->
{% extends './base.html' %}
{% load bootstrap4 %}

{% block content %}
    <h1>{{ message }}</h1>

    <!-- 投稿idに合わせたupdateを呼び出す -->
    <form action='{% url "bbs:update" article.id %}' method='post' class='form'>
        {% csrf_token %}
        {% bootstrap_form articleForm layout='horizontal' %}
        <button type='submit' class='btn btn-outline-primary'>保存する</button>
        <a href='{% url "bbs:detail" article.id %}' class='btn btn-outline-secondary'>戻る</a>
    </form>
{% endblock %}

view.pyに「/update」を追加

# myapp/bbs/views.py
def update(request, id):
    if request.method == 'POST':
        # POSTメソッドなら既存の投稿データを取り出す
        article = get_object_or_404(Article, pk=id)
        # 受け取ったデータを元にArticleFormオブジェクトを生成
        articleForm = ArticleForm(request.POST, instance=article)
        if articleForm.is_valid():
            articleForm.save()			# 正常なデータならデータベースへ格納

    context = {
        'message': 'Update article ' + str(id),
        'article': article,
    }
    return render(request, 'bbs/detail.html', context)	# 詳細ページを呼び出す

detail.htmlから「edit」と「delete」を呼び出す

<!-- myapp/bbs/templates/bbs/detail.html -->
<div>
    <a href='{% url "bbs:index" %}' class='btn btn-outline-primary'>一覧</a>
    <a href='{% url "bbs:edit" article.id %}' class='btn btn-outline-primary'>編集</a>
    <a href='{% url "bbs:delete" article.id %}' class='btn btn-outline-secondary'>削除</a>
</div>

一覧ページから削除ボタンを除去

<!-- myapp/bbs/templates/bbs/idnex.html -->
<table class='table table-striped table-hover'>
    {% for article in articles %}
        <tr>
            <td>{{ article.content }}</td>
            <td>{{ article.user_name }}</td>
            <td>
                <a href='{% url "bbs:detail" article.id %}' class='btn btn-outline-primary'>詳細</a>
                <!-- ここにあった削除ボタンのリンクは削除 -->
            </td>
        </tr>
    {% endfor %}
</table>

Webプログラミング入門

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