勉強会用資料です
今回は本家のチュートリアルから完全にそれてbootstrapのことについて説明していきます.
チュートリアルのチュートリアル
チュートリアル1
チュートリアル2
チュートリアル3
チュートリアル4
チュートリアル5
→ 他のチュートリアル
Bootstrapを使おう
htmlのデザイン難しいですよね.
デザイナーがビシっと綺麗なhtmlを用意してくれたらいいですが,tutorialには残念ながらそんなもの望めません.
かと言って,綺麗なcssを素人が1から作っていくのは大変です.
そんなあなたにbootstrap!
bootstrapは無料(商用利用も可!)で使えるレスポンシブデザインのcss,javascript,font, iconなどを提供するフレームワークです.
レスポンシブデザインはブラウザの大きさに合わせて自動的に表示幅や配置などを調整してくれるデザインのことです.
PC,スマートフォン,タブレットで同じhtmlを使い回すことができます.
本家
とほほのBootstrap入門
Bootstrapとは?(wikipedia)
djangoではbootstrapとの連携用のライブラリもありますし,cssやjavascriptの配置の練習にちょうどいいので
tutorialで扱っていきたいと思います.
Bootstrapのdownloadと配置
ソース→6ce33b05
さっそく本家のページから一式をDownloadしてきましょう.
↓ Downloadボタンを押すと飛ばされます
今回は一番左の Bootstrap を使います.
bootstrap-3.3.6-dist.zip
というzipファイルがdownloadできると思います.これを解凍しましょう.
次に,tutorialのmanage.py
と同じ階層にstatic
というフォルダを作り,そこに解凍したbootstrapの中身を全部放り込みます.
移動後のファイル構成は以下のようになっているはずです.
(tutorial)$ tree .
├── db.sqlite3
├── manage.py
├── polls
│ ├── __init__.py
│ ├── ...
│ └── views.py
├── requirements.txt
├── static # 今回追加したディレクトリ
│ ├── css
│ │ ├── bootstrap-theme.css
│ │ ├── bootstrap-theme.css.map
│ │ ├── bootstrap-theme.min.css
│ │ ├── bootstrap-theme.min.css.map
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ └── js
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ └── npm.js
└── tutorial
├── __init__.py
├── ...
└── settings.py
djangoの設定変更
ソース→73d94740a
公式ドキュメント(英語)
公式ドキュメント(日本語,ただしdjango1.4)
チュートリアルで作ってきた http://localhost:8000/polls/ のようなページはDBの内容によって表示される内容が変わります.
このようなページ(URL)は動的なページ,動的ページ,動的コンテンツなどと呼ばれます.
それに対し,bootstrap.cssのようなcssやjsファイルは同じURLにアクセスすると常に同じ内容が返って来ます.
このようなページは静的なページ,静的コンテンツ,静的ファイルなどと呼ばれます.
動的コンテンツはアクセス毎に内容が変化するため,プログラム(pythonだったりrubyだったりphpだったり)を実行し,
ページ内容を変更する必要があります.
これに対して静的コンテンツは内容がかわらないため,アクセス毎に問い合わせをする必要がなく,
ブラウザなどに内容がキャッシュされます.
キャッシュすることにより,読み込みが高速化され,サーバの負荷が軽減されますが,内容の変更が反映されないなどのデメリットもあります.
djangoでは画像ファイル,cssファイル,javascriptファイルなどの静的ファイルはstatic files
として管理し,
運用時に一箇所にまとめることで管理しやすくしています.
一箇所にまとめる方法は
$ ./manage.py collectstatic
ですが,runserver で実行している間はこのコマンドを使う必要はありません.
deploy(サーバの設置)の説明もそのうちする予定なので,その時に詳しく説明します.
staticファイルは各アプリの下にあるstatic
ディレクトリの中がかき集められ,urls.pyにURLを追加することなく,
http://localhost:8000/static/
でアクセスすることができます.
本チュートリアルでは取り扱う予定はありませんが,polls独自のcssが必要な場合は
polls/static/polls/css/polls.css
のように設置し,
http://localhost:8000/static/polls/css/polls.css
のようにアクセスします.
ちなみにdjango管理サイトようの静的ファイルは django/contrib/admin/static/admin/
以下にあります.
テンプレートの時も
polls/templates/polls/
のようになっています.
templates/polls/
やstatic/polls/
のようにアプリ名を付けるのは冗長に感じるかもしれませんが,
名前衝突の回避や,コード的なメリットがありこのような構成になってるようです.
ただし,デフォルトで探索されるstatic
ディレクトリはアプリ(settings.pyの中のINSTALLED_APPS)の下にある
staticファイルのみです.
今回はmanage.py
と同じ階層にstatic
ディレクトリを作成したため,このディレクトリも読み込ませるように設定を追加する必要があります.
静的ファイルが置かれているディレクトリを追加するには,STATICFILES_DIRS
に追加したいディレクトリをタプル(or リスト)で指定します.
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
テンプレートの修正
ソース→63cdd0cc
template内から静的ファイルを読みこませるときは{% static %}
というテンプレートタグを使います.
例えばbootstrap.css
を読み込むときは
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}" />
のように書きます.
{% static %}
タグを使うには最初に {% load staticfiles %}
と書く必要があります.
手始めに一覧ページでbootstrap.cssを読み込ませてみます.
{% load staticfiles %} <!-- ← 追加 -->
<html>
<head> <!-- ← 追加 -->
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}" /> <!-- ← 追加 -->
</head> <!-- ← 追加 -->
<body>
<table border="1">
<tr>
<th>質問内容</th>
<th>公開日</th>
<th></th>
</tr>
{% for question in questions %}
<tr>
<td>{{ question.question_text }}</td>
<td>{{ question.pub_date }}</td>
<td><a href="{% url 'polls:detail' question.pk %}">詳細画面へ</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
なんかちっさくなっただけでcssの恩恵を感じませんね.
cssはhtmlのタグにclass
を指定することで真価を発揮します.
とりあえず試しにテーブル用のcssを当ててみましょう.
bootstrapのページによれば,tableクラスをつければtable用のデザインがあたるようです.
ついでにstripedも当ててみます.
...
<body>
<table border="1" class="table table-striped">
<tr>
<th>質問内容</th>
...
はい.このようにtableタグにclassを追加するだけで,簡単にそれっぽいデザインになりました.
テンプレートの拡張
公式ドキュメント(英語)
公式ドキュメント(日本語,ただしdjango1.4)
すでに使用しているテンプレートですが,継承してこそ真価を発揮します.
qiita, bootstrapドキュメント, djangoドキュメント,どこでもいいですが,どのサイトでも
ページを移動した時のヘッダやフッタは基本的に同じで,ページの中身だけが変化しているでしょう?
サイト内の統一性を考えれば当然ですが,同一サイト内では枠組みは同じで,一部分のみが変化します.
クラス汎用ビューを使ったときと同じように,ベースのテンプレートを作り,そのテンプレートを継承して
変化させたい部分だけをオーバーライドすることでコード量を減らし,変更に強いサイトを作ることができるようになります.
本節ではベースとなるテンプレートを作成し,チュートリアルで作成したテンプレートを置き換え,
簡単にbootstrapのデザインを適用させる準備をします.
ベーステンプレートの作成
ソース→741a853886
何はともあれ,ベースとなるテンプレートを用意しましょう.
static
ファイルの時もそうでしたが,templateの場合もtemplates
ディレクトリをmanage.py
と同じ階層に用意し,
それをベースのテンプレート置き場とします.
dashboard
など,如何にもベースになり得そうなアプリがある場合はそこに置くのもありだと思います.
templateの場合もstatic
の時と同じく,デフォルトではアプリの下にあるtemplates
ディレクトリしか見てくれません.
static
の時と同じようにsettings.py
を変更しましょう.
templateの場合はTEMPLATES
の中のDIRS
に追加したいディレクトリを記述します.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # ←← ここの中身を追加
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
次にtemplates
の中にbase.html
を作りましょう.
肝心の中身ですが,bootstrapのstarter-templateを使うことにします.
中身の修正が必要ですが,とりあえず同じものをベタッと貼ってそれをbase.html
として保存しましょう.
継承の前準備とベースの修正
ソース→2cca1ce20eb
とりあえず見ながら修正するようにしたいので,
polls/templates/polls/index.html
をbase.html
を継承するように修正しましょう.
やることは簡単で,頭に{% extends "base.html" %}
と書くだけです.
{% extends "base.html" %}
{% load staticfiles %}
<html>
...
この状態でhttp://localhost:8000/polls/
を見ると以下のようになっているはずです.
はい,ぐちゃぐちゃですね.
cssのパスがちゃんとあたってないからこうなってます.
とりあえずcssやjsのパスを直して表示させたのがこちら.
htmlの修正はdjangoとはあまり関係ないのでさっくり飛ばします.ソースで確認してください.
変更用blockの追加と上書き
ソース→8a3fe39358
やっと本筋です.
今画面に出ている「Bootstrap starter template」の部分に質問一覧を表示させるようにしてみましょう.
htmlの<div class="container">
の中身がこの部分に相当するので,ここを消去し,
継承先で上書きできるようにします.
<div class="container">
<div class="starter-template">
<h1>Bootstrap starter template</h1>
<p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
</div>
</div><!-- /.container -->
↓
<div class="container">
{% block contents %}{% endblock %}
</div><!-- /.container -->
中身をごっそり消して{% block %}
タグを用意します.
名前はなんでもいいですが,ここではcontents
としています.
続いて継承先のほうのテンプレートを修正します.
{% extends "base.html" %}
{% load staticfiles %}
<html>
<head>
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}" />
</head>
<body>
<table border="1" class="table table-striped">
<tr>
<th>質問内容</th>
<th>公開日</th>
<th></th>
</tr>
{% for question in questions %}
<tr>
<td>{{ question.question_text }}</td>
<td>{{ question.pub_date }}</td>
<td><a href="{% url 'polls:detail' question.pk %}">詳細画面へ</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
↓
{% extends "base.html" %}
{% block contents %}
<table border="1" class="table table-striped">
<tr>
<th>質問内容</th>
<th>公開日</th>
<th></th>
</tr>
{% for question in questions %}
<tr>
<td>{{ question.question_text }}</td>
<td>{{ question.pub_date }}</td>
<td><a href="{% url 'polls:detail' question.pk %}">詳細画面へ</a></td>
</tr>
{% endfor %}
</table>
{% endblock contents %}
htmlタグ,headタグ,bodyタグは継承元(templates/base.html)で設定しているので必要ありません.
<table>
タグの中身をcontents
内に配置したいので,
{% block contents %}
を頭に,{% endblock contents %}
を最後につけています.
{% endblock %}
には引数は必要ありませんが,blockの中身が大きくなるとどのblock
を閉じたか分からなくなるので
{% block %}
と{% endblock %}
が離れている場合はなるべく書くようにしたほうがいいでしょう.
ちなみに{% endblock content %}
のように{% block contents %}
と名前が対になっていない場合はエラーになります.
さて,どうなったかブラウザで確認してみましょう.
テーブルがヘッダにくっついちゃってますが,まぁ及第点ではないでしょうか.
base.htmlの修正とpolls/base.htmlの追加
ソース→e722778ee
せっかくTOPページが出来たので http://localhost:8000/
でアクセスできるように
tutorial/urls.py
を編集しておきます.
と言っても,実際にTOPページ用のviewがあるわけではないのでとりあえずpolls/
と同じ内容にしておきます.
名前はindex
にしてますが,top
やhome
など好みの名前をつけてください.
from django.conf.urls import include, url
from django.contrib import admin
from polls.views import index # ← 追加
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^polls/', include('polls.urls', namespace='polls')),
url(r'^$', index, name='index'), # ← 追加
]
で,base.html
の文言やリンクを修正していきます.
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
...
<a class="navbar-brand" href="{% url 'index' %}">Tutorial</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="{% url 'polls:index' %}">polls</a></li>
<li class=""><a href="{% url 'admin:index' %}">admin</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
ここでちょっと一工夫.<div id="navbar">
内のpollsの<li>
にclass="active"
がついています.
active
がつくと,ヘッダが以下のようにハイライトされ,どこのページにいるかわかりやすくなります.
polls内のtemplateでは常にactiveを付けたいですが,他のアプリの場合はハイライトさせたくありません.
そこでこの部分を{% block %}にし,書き換えれるようにします.
<li class="{% block nav_polls %}{% endblock %}"><a href="{% url 'polls:index' %}">polls</a></li>
ただし,このままではpolls/templates/polls
内のテンプレート全てを変更しないといけなくなります.
そこで,polls/templats/polls/base.html
を作成し,polls/templats/polls
下のテンプレートでは
polls/base.html
を継承するようにします.
base.html
└ polls/base.html
├ polls/index.html
├ polls/detail.html
└ polls/results.html
{% extends "base.html" %}
{% block nav_polls %}active{% endblock %}
nav_pollsにactiveを付ける
{% extends "polls/base.html" %}
base.htmlではなくpolls/base.htmlを継承する
ついでにdetail.html
とresults.html
も継承するように変更しておきましょう.
直し方はindex.html
の時と同じく,一行目に{% extends "polls/base.html" %}
をつけ,
中身を{% block contents %}
で囲むだけです.
TOPページ
選択ページ
結果ページ
Django Bootstrap
ここまではdjangoのtemplate機能を使って自分でbootstrapのcssを充てる作業だったので
あまり恩恵を感じなかったと思います.
本章ではdjangoのデザインを簡単に置き換えることができるdjango-bootstrap
を紹介します.
https://github.com/dyve/django-bootstrap3
http://django-bootstrap3.readthedocs.org/en/latest/index.html
導入とFormの置き換え
ソース→498065d9fff
ソースをcheckoutsして試す場合は
pip install -r requirements.txt
を実行してください.
pipで簡単に入ります.
bootstrapのバージョンの関係で,名前は
django-bootstrap3
になります.
"3"のつけ忘れに注意.
(tutorial)$ pip install django-bootstrap3
Collecting django-bootstrap3
Downloading django-bootstrap3-6.2.2.tar.gz
Building wheels for collected packages: django-bootstrap3
Running setup.py bdist_wheel for django-bootstrap3 ... done
...
Successfully built django-bootstrap3
Installing collected packages: django-bootstrap3
Successfully installed django-bootstrap3-6.2.2
次にtutorial/settings.py
を開き,INSTALLED_APPSにbootstrap3
を追加します.
INSTALLED_APPS = (
'django.contrib.admin',
...
'django.contrib.staticfiles',
'bootstrap3', # ← 追加
'polls',
)
インストール作業はこれで終わりです.
さっそくFormを書き換えてみましょう.
今回はpolls/templates/polls/detail.html
で出力しているformを書き換えてみます.
{{ form }}
でformを出力する代わりに,{% bootstrap_form %}
テンプレートタグを使ってformを出力させます.
使用前にbootstrap3
をloadしておく必要があります.
{% extends "polls/base.html" %}
{% load bootstrap3 %} <!-- ← 追加 -->
{% block contents %}
<h1>{{ question.question_text }}</h1>
<form action="" method="post">
{% csrf_token %}
{% bootstrap_form form %} <!-- ← 修正 -->
<input type="submit" value="Vote" />
</form>
{% endblock contents %}
1行の追加,1行の修正です.
画面上でどうなったか確認してみましょう.
無駄についてた黒ポチが消えました.
なにも選択せずにVote
ボタンを押してみてください.
こんな感じで,「いかにも」なエラーチェックが自動で付きます.
選択肢にない値を送信するとこんな感じになります.
builtin設定
ソース→1f19af40
さて,たった2行書くだけでFormが見違えるように綺麗になりました.
が,いちいち{% load bootstrap3 %}
を書くには面倒くさいですよね.
bootstrap3
用のテンプレートタグを自動で読み込むようにsettings.py
に記述しましょう.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'builtins': [
'bootstrap3.templatetags.bootstrap3', # ←←← これを追加
],
},
},
]
ちなみに,ここに書けるのはdjango1.9
からです.
django1.8
以前は django.template.base.add_to_builtins
メソッドを使うことで同じことができます.
{% load %}
タグの時はbootstrap3
だけでしたが,builtinsで指定する場合はbootstrap3.templatetags.bootstrap3
のようにtemplatetags
までのPATHも含めないと行けないので注意してください.
polls/templates/polls/detail.html
からload
の記述を消してエラーが出ないことを確かめましょう.
message表示
ソース→a2d08a455
今は選択肢を選んでVoteすると結果ページに飛ぶだけの味気ないものになっています.
結果ページにユーザが何を投票したのか出力してみましょう.
djangoドキュメント(英語)
djangoドキュメント(日本語)
djangoでは,次のページへなんらかの情報を持ちまわる仕組みをmessages
として提供しています.
使用するにはsettings.pyのINSTALLED_APPSにdjango.contrib.messages
が含まれてる必要があります.
adminなどと同じようにデフォルトで入ってるのでsettingsの修正は必要ないです.
まずはメッセージを追加しましょう.
今回は投票した時に,どの選択をしたかを出したいので,polls/views.py
のform_valid
を編集します.
メッセージの追加方法は messages.success
関数にrequest
と表示したいメッセージを渡すだけです.
success
の他に,debug
,info
,warning
,error
があります.
from django.contrib import messages
...
class Detail(SingleObjectMixin, FormView):
...
def form_valid(self, form):
form.vote()
choice = form.cleaned_data['choice']
messages.success(self.request, '"%s"に投票しました' % choice)
return super().form_valid(form)
voteの戻り値でメッセージ(もしくはchoiceのインスタンス)を返すようにするともう少しスムーズになります.
続いて表示です.
メッセージの表示は全部のページで行いたいので,base.html
に追加します.
django-bootstrap3
ではメッセージの表示もサポートしているので,これも簡単にかけます.
...
<div class="container">
{% if messages %}
{% bootstrap_messages messages %}
{% endif %}
{% block contents %}{% endblock %}
</div><!-- /.container -->
...
こんな感じで,メッセージが表示されるようになりました.
右側にある x ボタンを押すとこのメッセージは消えます.
次のチュートリアルではテストについて説明していきます(本家チュートリアル5相当です) [次のチュートリアルへ](http://qiita.com/maisuto/items/cce169a2455b116e2f82)