テンプレートの共通化
共通部分をまとめると、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でフォームを使うための基本的な操作を理解していきます。
フォームを利用する流れ
- forms.py: フォームのクラスで、データ形式を定義する
- views.py: フォームから受け取ったデータを処理して、フォームオブジェクトをテンプレートに渡す
- 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/new | new() | 新規作成 |
bbs/create | create() | 新規投稿 |
bbs/(id)/edit | edit() | 編集 |
bbs/(id)/update | update() | 更新 |
bbs/(id)/delete | delete() | 削除 |
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: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>