超初心者用: Djangoでウェブアプリを作る ④追加ページの作成
前回はホームページを作りました。
今回はそれ以外のページを作っていきます。トピックのリストのページとそれぞれのトピックの Entry のページの2ページを作ります。それぞれのページにURLパターンをつけ、view を書き、template を書きます。
その前にすべてのテンプレートで使う base template を作ります。
テンプレートの継承
ウェブサイトを作るとき、どのページでも必ず存在する要素があります。例えばロゴとかナビゲーションバーとか。このコードを枚描くのではなく、base template として繰り返すことができます。このようにすることで、時間がだいぶ節約できます。
Parent template
index.html と同じ階に base.html という template を作成します。このファイルがすべてのページに適応されることになります。つまり、すべての template が base template を受け継ぎます。今回は、タイトルだけです。タイトルにホームページへのリンクをつけましょう。
<!--base.html--> <p> <a href="{% url 'practicing_logs:index' %}">Practicing Log</a> </p> {% block content %}{% endblock content %}
リンクを生成するには、tamplate tag の {% %} を使います。url 'practicing_logs:index' は practicing_logs という app_name の practicing_logs/urls.py 内の index という名前のURL を意味しています。
URLを直接入力するのではなくこのようにかくことで、URLを変更したときに修正する必要がなくなります。つまり URLを変えるときは、urls.py を変えるだけでOKです。
すべてのページがこの base template を持つことになるので、もうタイトルの記入は必要ありません。この template を引っ張ってくるだけです。
{% block content %}{% endblock content %}
これは content と呼ばれ、ほかページの内容がここに追加されます。
Child template
base template ができたので、ホームページを書き換えましょう。
{% extends "practicing_logs/base.html" %} {% block content %} <p>Practicing Log helps you keep track of your pracitcing, for any topic you're placticing about.</p> {% endblock content %}
{% extends "practicing_logs/base.html" %}
元の index.html と比較すると、タイトル部分が base template に置き換わっています。ほかの template を受け継ぐには、{% extends %} tag が必要になります。今回は practicing_logs 内の base.html を受け継ぐため、 practicing_logs/base.html と表記します。
{% block content %}
先ほど見かけた content が早速登場しました。parent template から受け継いでいない要素はすべてこの中に入ります。今回は文章が入ります。最後には {% endblock content %} tag で閉じます。
このように、すべてのページにおいて変わらない要素を別の template で作ることで、子ページには呼ぶだけで利用することができます。また、編集をしたいときには base template のみ編集するだけで全ページに適応されます。
Topics pageを作成する
では追加ページを作る本題に入っていきます。まずはトピックページです。このページでは、ユーザーが入力したすべてのトピックをリストとして表示させます。つまりデータを扱うことになります。
topics URL pattern
まずはトピックページの URL を定義します。できるだけシンプルなものにしましょう。今回は topics にします。つまり、http://localhost:8000/topics/です。practicing_logs/urls.py を変更します。
#practicing_logs/urls.py urlpatterns = [ # Home page url(r'^$', views.index, name='index'), #-------------------ここから下を追加--------------------- #topics のリンク url(r'^topics/$', views.topics, name='topics'), ]
「r'^topics/$」 は topics/ のURL を表しています。つまり base URL + topics/ となる URL の時に適応されます。URL が一致するときは views.py の topics() を呼びます。
Topics view
topics() function はデータベースからデータを取得し、template に送る必要があります。
# views.py from django.shortcuts import render <-- すでにある from .models import Topic def index(request): <-- すでにある --省略-- def topics(request): """すべての topics を表示する""" topics = Topic.objects.order_by('date_added') context = {'topics': topics} return render(request, 'practicing_logs/topics.html', context)
from .models import Topic
まずは必要となるデータに基づいたモデルをインポートします。
def topics(request):
topics() function は request parameter が必要です。
topics = Topic.objects.order_by('date_added')
ここでTopicを通すことでデータベースからデータを取得します。date_added attribute によって追加された順に topics に保存していきます。
context = {'topics': topics}
template に送るための context を定義します。context は dictionary で、key がデータを呼ぶための名前で、value がデータ自身です。この場合、1つの key に対して複数の value が存在し、それらは set として保存されています。
return render(request, 'practicing_logs/topics.html', context)
render() を使って context の内容を practicing_logs/topics.html に送っています。
Topics template
トピックページの template は context のデータを受け取っています。だから、ここで使うことができます。index.htmlと同じ階にtopics.htmlを作成してください。
# topics.html {% extends "practicing_logs/base.html" %} {% block content %} <p>Topics</p> <ul> {% for topic in topics %} <li>{{ topic }}</li> {% empty %} <li>No topics have been added yet.</li> {% endfor %} </ul> {% endblock content %}
まずは {% extends %} tag を使って、このページにタイトルを付けます。{% block content %} からこのページ独特の内容を書き込みます。リストとして表示されるように unordered list の
- を作ります。
{% for topic in topics %}
ここでは for loop を使って context 内の topics をループします。ループの終わりには{% endfor %}と書きます。
ループの中では topic を順番に表示させます。{{ topic }}と書くことで key である topic の value が表示されます。
{% empty %}
これは if節でもしトピックがなかったらこれ以降を実行します。今回はトピックがないというメッセージを表示させます。
では、ナビゲーションとして base template にトピックページを追加しましょう。
# base.html <p> <a href="{% url 'practicing_logs:index' %}">Practicing Log</a> <a href="{% url 'practicing_logs:topics' %}">Topics</a> </p>
practicing_logs という名前の app_name から topics という名前の URL パターンを示しています。
トピックページはこんな感じになっているはずです。
それぞれのTopicページ
次は、トピック全体ではなく1つのトピックに焦点を置いたページを作っていきます。このページにふくまれるのは、トピック名とそのトピックの entries です。再び URLパターンを作成していきます。さらに先ほど作ったトピックページも編集し、これから作るぺージに飛べるようにします。
Topic URL pattern
これから作るトピック専用ページの URLパターンは今までのURLパターンと少し異なります。トピックの id を利用します。例えば、id が1であるChess トピックページを見たいときは、http:// localhost:8000/topics/1/となります。
# practicing_logs/urls.py urlpatterns = [ #--省略-- # トピックの詳細ページ url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'), ]
r'^topics/(?P<topic_id>\d+
r は string を raw string として扱うように示しています。(?P<topic_id>\d+ の「\」までの中身はトピックの id 意味します。
このパターンに合うURL が見つかったときに、id とともに topic() function を呼びます。
Topic view
topic() function はトピック名とそれの基づいたすべての entries のデータを取得します。
# views.py --省略-- def topic(request, topic_id): """1つのトピック名とそれのすべての entries を表示""" topic = Topic.objects.get(id=topic_id) entries = topic.entry_set.order_by('-date_added') context = {'topic': topic, 'entries': entries} return render(request, 'practicing_logs/topic.html', context)
今回は request だけではなく topic_id という parameter を使います。topic_id は (?P<topic_id>\d+) で取得したものです。
topic = Topic.objects.get(id=topic_id)
get() を使ってその id のトピック名を取得します。
entries = topic.entry_set.order_by('-date_added')
そのトピックに関連した entries を時間の新しい順「-」に取得します。こうすることで最新情報を一番上に表示することができます。
取得したデータを context dictionary に保存します。最後に template topic.html に context を送ります。
Topic template
この template ではトピック名と entries を表示させる必要があります。さらに entry が1つもなかった場合、その旨も知らせなければなりません。
# topic.html {% extends 'practicing_logs/base.html' %} {% block content %} <p>Topic: {{ topic }}</p> <p>Entries:</p> <ul> {% for entry in entries %} <li> <p>{{ entry.date_added|date:'M d, Y H:i' }}</p> <p>{{ entry.text|linebreaks }}</p> </li> {% empty %} <li> There are no entries for this topic yet. </li> {% endfor %} </ul> {% endblock content %}
まずはいつも通り、base template を組み込みます。そして block content が始まることを知らせます。
Topic: {{ topic }}
一番上にトピック名を表示させます。{{ topic }} は context から引っ張ってきます。
{% for entry in entries %}
{{ entry.date_added|date:'M d, Y H:i' }}
{{ entry.text|linebreaks }}
for loop を使って entries の日付と内容を1つずつ表示させていきます。「|」は表示方法のフォーマットを定義しています。
{% empty %}
There are no entries for this topic yet.
entry がない場合はこちらが実行されます。最後に for loop を閉じて終わり。
Topicsページにリンクをつける
個々のトピックページを見るためには、全体のトピックページにリンクをつけなければなりません。
#--省略-- {% for topic in topics %} <li> <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a> </li> {% empty %} #--省略--
topic の名前によって適切な URL を使わなければなりません。URL パターンは topic_id を必要としているので、URL tag に topic_id を付け加えました。今、http://localhost:8000/topics/1/ に飛ぶと、Chess の詳細が見えるはずです。