ページの改良
はじめに
前回に引き続きトップページを見やすい形にしていきます。やることは、本を登録した新着順、レビューの平均点順に並び替えをします。それからページネーションをつけて1ページに2冊ずつ本を見れるようにします。
トップページのデータの並び替え
新着順に並び替え
新着順に並び替えるにはbook/views.pyを以下のように編集します。
def index_view(request): object_list = Book.objects.order_by('-id') # 修正 return render( request, 'book/index.html', {'object_list': object_list})
修正した-idとすることで新着順にしています。本の追加した順にidは1,2,3...と増えていくのでidの古いものからとすると、新しい本からというようになります。
レビューの平均点順に並び替え
平均点順にはbook/views.pyを以下のようにします。
from django.db.models import Avg def index_view(request): object_list = Book.objects.all ranking_list = Book.objects.annotate(avg_rating=Avg('review__rate')).order_by('-avg_rating') return render( request, 'book/index.html', {'object_list': object_list, 'ranking_list': ranking_list}, )
追加したAvg('review__rate')を使うことでデータの平均値を取ります。どのデータの平均値をとるのかは、'review__rate'で指定します。つまりreviewが対象とするモデル、rateがそのフィールドを示しています。
できればbook/templates/book/index.htmlを修正し、投稿順とランキング順に並び替えます。
{% extends 'base.html' %} {% block title %}本棚アプリケーション{% endblock %} {% block h1 %}本棚アプリケーション{% endblock %} {% block content %} <div class="row"> <div class="col-9"> {% for item in object_list %} <div class="p-4 m-4 bg-light border border-success rounded"> <h2 class="text-success">{{ item.title }}</h2> <img src="{{ item.thumbnail.url }}" class="img-thumbnail" /> <h6>カテゴリー: {{ item.category }}</h6> <div class="mt-3"> <a href="{% url 'detail-book' item.pk %}">詳細へ</a> </div> </div> {% endfor %} </div> <div class="col-3"> {% for ranking_book in ranking_list %} </div class="p-4 m-4 bg-light border border-success rounded"> <h3 class="text-success h5">{{ ranking_book.title }}</h3> <img src="{{ ranking_book.thumbnail.url }}" class="img-thumbnail" /> <h6>評価:{{ ranking_book.avg_rating}点</h6> <a href="{% url 'detail-book' ranking_book.id %}">評価を見る</a> </div> {% endfor %} </div> </div> </div> {% endblock content %}
これで新着順、評価順に並び替えることができました。少し見た目を整えるために評価の小数点を修正します。
book/templates/book/index.htmlを以下のように修正します。
{% for ranking_book in page_obj %} </div class="p-4 m-4 bg-light border border-success rounded"> <h3 class="text-success h5">{{ ranking_book.title }}</h3> <img src="{{ ranking_book.thumbnail.url }}" class="img-thumbnail" /> <h6>評価:{{ ranking_book.avg_rating|floatformat:2 }}点</h6> # 修正 <a href="{% url 'detail-book' ranking_book.id %}">評価を見る</a> </div> {% endfor %}
追加した|floatformat:で小数点以下を四捨五入することができます。その後ろに2とすることで小数点以下第2位まで見れるようにしました。
書籍の詳細にレビューを追加する
ここでは複数のレビューを追加、閲覧できるようにします。やりかたはbook/templates/book/detail.htmlに以下を追加します。
<p>{{ object.text }}</p> <div class="border p-4 mb-2"> # 追加 {% for review in object.review_set.all %} # 追加 <div> # 追加 <h3 class="h4">{{ review.title }}</h3> # 追加 <div class="px-2"> # 追加 <span>(投稿ユーザー: {{ review.user.username }})</span> # 追加 <h6>評価: {{ review.rate }}点</h6> # 追加 <p>{{ review.text }}</p> # 追加 </div> # 追加 </div> # 追加 {% endfor %} # 追加 </div> # 追加 <a href="{% url 'list-book' %}" class="btn btn-primary">一覧へ</a>
これで一度に複数のレビューに対応することができます。
以下のように複数のレビューが表示されるでしょう。
ページネーションの作成
本棚アプリケーションで本が何百冊となった場合、10冊ずつの閲覧ができたほうが見栄えが良いでしょう。ここでは2冊ずつでページネーションを作成していきます。
今回はfunciton-based viewとclass-based viewの場合の実装をしていきます。
funciton-based viewでのページネーションの作成
まずはbook/views.pyとbook/consts.pyに以下を追加します。
from django.core.paginator import Paginator # 追加 from .consts import ITEM_PER_PAGE # 追加 def index_view(request): object_list = Book.objects.all ranking_list = Book.objects.annotate(avg_rating=Avg('review__rate')).order_by('-avg_rating') paginator = Paginator(ranking_list, ITEM_PER_PAGE) # 追加 page_number = request.GET.get('page',1) # 追加 page_obj = paginator.page(page_number) # 追加 return render( request, 'book/index.html', {'object_list': object_list, 'ranking_list': ranking_list, 'page_obj': page_obj}, # 追加 )
MAX_RATE = 5 ITEM_PER_PAGE = 2 # 追加
from .consts import ITEM_PER_PAGEで一つのページに表示する本の数を指定するために定義しておき、ITEM_PER_PAGE = 2と2つずつ表示させるようにしています。ここを違う数にすると10冊ずつのように変更ができます。
次はhtmlファイルの中にページネーションの仕組みをいれていきます。そのため、以下のコマンドで新たなファイルを作成しましょう。
mkdir book/templates/book/components mkdir book/templates/book/components/paginations.html
作成したファイルに以下の記述をします。
{% if page_obj.has_other_pages %} <ul class="list-unstyled m-0 d-flex justify-content-between"> {% if page_obj.has_previous %} <li><a href="?page={{ page_obj.previous_page_number }}"><<前へ</a></li> {% else %} <li class="text-muted"><<前へ</li> {% endif %} {% if page_obj.has_next %} <li><a href="?page={{ page_obj.next_page_number }}">次へ>></a></li> {% else %} <li class="text-muted">次へ>></li> {% endif %} </ul> {% endif %}
基本的にはpage_objを使って条件分岐を行っています。
<a href="?page={{ page_obj.previous_page_number }}"><<前へ</a>
の部分で前のページがある場合は"前へ"と表示し、前のページのリンクを挿入することでページネーションの実装を行っています。
次への部分も基本的には同じです。
あとは記述したcomponentsファイルをhtmlファイルに組み込みます。book/templates/book/index.htmlを以下のように変更します。
<div class="col-3"> <h2>評価順TOP2</h2> # 変更 {% for ranking_book in page_obj %} # 変更 </div class="p-4 m-4 bg-light border border-success rounded"> <h3 class="text-success h5">{{ ranking_book.title }}</h3> <img src="{{ ranking_book.thumbnail.url }}" class="img-thumbnail" /> <h6>評価:{{ ranking_book.avg_rating|floatformat:2 }}点</h6> <a href="{% url 'detail-book' ranking_book.id %}">評価を見る</a> </div> {% endfor %} {% include 'book/components/pagination.html' %} # 変更 </div>
これで一度確認してみましょう。
前へのリンクははじめのページなのでありませんが、次へはあります。これでページネーションの作成ができました。
class-based viewでのページネーションの作成
次は、class-based viewでページネーションの作成をします。
book/views.pyに以下の記述を追加します。
class ListBookView(LoginRequiredMixin, ListView): template_name = 'book/book_list.html' model = Book paginate_by = ITEM_PER_PAGE # 追加
実はこれだけでできます。あとはhtmlにページネーションに関する記述を追加します。book/templates/book/book_list.htmlに以下の記述を追加します。
{% extends 'base.html' %} {% block title %}書籍一覧{% endblock %} {% block h1 %}書籍一覧{% endblock %} {% block content %} {% for item in object_list %} <div class="p-4 m-4 bg-light border border-success rounded"> <h2 class="text-success">{{ item.title }}</h2> <h6>カテゴリー:{{ item.category }}</h6> <div class="mt-3"> <a href="{% url 'detail-book' item.pk %}">詳細へ</a> </div> </div> {% endfor %} {% include 'book/components/pagination.html' %} # 追加 {% endblock content %}
これで同じように確認してみると良いでしょう。class-based viewでもページネーションの作成ができました。
おわりに
今回は新しい順、評価順に並び替えて表示するような変更を行いました。また、2冊ずつでページネーションをつけて見やすいページになるようにしました。
これで本棚アプリケーションの作成はおおよそ終了しました。
次回は作成した本棚アプリケーションをHerokuで公開します。