「Django + microCMSでつくるブログサイト」シリーズの5番目の記事です。
今回はタグでの絞り込み検索を実装します。
microCMSのGETリクエストでは絞り込み検索を`filters`パラメーターを使って行えます。
ここも公式ブログを参考にしましょう。
filters: コンテンツを絞り込んで取得できるようになりました
今回はタグでの絞り込みをしたいですが、記事APIのタグフィールドは複数参照としています。
注意点は複数参照としている場合はcontains
を使うことです。
複数のコンテンツ参照では使用できるのは [contains] のみです。また、以下の例のように値部分にはコンテンツのidが入ります
例えばタグのIDがhogehoge
のタグ名Python
が含まれる記事を取得する際のリクエストURLは下記のようになります。
https://your-service-id.microcms.io/api/v1/post?filters=tag[contains]hogehoge
前回の記事でページネーションを実装しました。
考えなければいけないのは、通常の記事一覧とタグで絞り込まれている際の記事一覧を別に処理する、ということです。
例えば5つ記事がある、3つの記事にPython
のタグがついている、1ページに2つの記事を表示するとしている、といった場合で考えます。
通常の記事一覧では1,2,3と3ページ分のページ送りが必要です。
Python
が選択されている際は、1,2の2ページ分のページ送りとなります。
今回も明示的にurlパターンを作ります。
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='index'),
path('page/<str:page>/', views.post_list, name='index_with_page'),
path('tag/<str:tag_id>/page/<str:page>/', views.post_list, name='index_with_tag'), # 追加
path('post/<slug:slug>/', views.post_detail, name='post_detail')
]
ページとセットでURLを指定する形です。
次にviews.py
を以下のようにします。
テンプレートにタグの一覧をわたしておくようにします。
そうして、タグがクリックされたら、タグIDを受け取り、絞り込みリクエストを実行する形です。
# blog/views.py
from django.shortcuts import render
import requests
from django.conf import settings
from django.http import Http404
import math
def post_list(request, **kwargs):
"""記事一覧"""
limit = 2
current_page = kwargs.get('page', 1)
offset = (int(current_page) - 1) * limit
end_point = f'/post?limit={limit}&offset={offset}'
# タグIDが渡された場合はエンドポイントを更新
tag_id = kwargs.get('tag_id', None)
if tag_id:
end_point += f'&filters=tag[contains]{tag_id}'
url = getattr(settings, "BASE_URL", None)
api_key = getattr(settings, "API_KEY", None)
headers = {'X-API-KEY': api_key}
res = requests.request(method='GET',
url=url + end_point,
headers=headers)
# タグの一覧を取得
tags_res = requests.request(method='GET',
url=url + '/tag',
headers=headers)
# 名前昇順で並び替え
tag_list = tags_res.json()['contents']
tag_list.sort(key=lambda x: x['name'])
total_count = res.json()['totalCount']
num_page = math.ceil(total_count / limit)
context = {
'post_list': res.json()['contents'],
'num_page': range(1, num_page + 1),
'current_page': int(current_page),
'last_page': num_page,
# トップページの一覧用
'tag_list': tag_list,
# ページャーの処理用
'tag_id': tag_id
}
return render(request, 'blog/index.html', context)
タグの一覧GETについて、microCMSにはorder
パラーメーターで並び替えもできるのですが、下記のフィールドにしか対応していません。
なので、key引数を用いて、名前の順で並び替えたうえでテンプレートに渡すようにしています。
components
内にsearch-tag.html
を作成します。
<!-- blog/templates/blog/components/search-tag.html -->
<div style="margin-bottom:6rem;">
<h2 class="tag-heading">タグ一覧</h2>
<div class="inline">
{% for tag in tag_list %}
<span>
{% if tag.id == tag_id %}
<a href="{% url 'blog:index' %}" class="tag-badge tag-active">{{ tag.name }}</a>
{% else %}
<a href="{% url 'blog:index_with_tag' tag.id 1 %}" class="tag-badge">{{ tag.name }}</a>
{% endif %}
</span>
{% endfor %}
</div>
</div>
タグが選択されている場合とそうでない場合に分けます。
選択中のタグがクリックされたら通常の一覧画面に戻る仕様です。
<!-- blog/templates/blog/index.html -->
{% extends "blog/base.html" %}
{% load blog %}
{% load static %}
{% block content %}
<div class="container" style="padding-top:112px;">
<!-- 読み込み -->
{% include "blog/components/search-tag.html" %}
{% for post in post_list %}
<article class="article">
...省略
</article>
{% endfor %}
{% include "blog/components/pagination.html" %}
</div>
{% endblock %}
pagination.html
を編集します。
ちょっとかっこ悪いですが、タグが選択されている場合とそうでない場合で、まるまるブロックを切り替えます。
タグが選択されている場合(=tag_idがテンプレートに存在したら)、新たに作成したURLパターンを適用してページ送りします。
<!-- blog/templates/blog/components/pagination.html -->
<!-- タグが選択されている場合 -->
{% if tag_id %}
<div class="pagination">
<ul>
{% if current_page == 1 %}
<span class="pagination-link prev-off"><li><</li></span>
{% else %}
<a href="{% url 'blog:index_with_tag' tag_id current_page|add:'-1' %}" class="pagination-link"><li><</li></a>
{% endif %}
{% for num in num_page %}
{% if num == current_page %}
<span class="pagination-link is-active"><li>{{ num }}</li></span>
{% else %}
<a href="{% url 'blog:index_with_tag' tag_id num %}" class="pagination-link"><li>{{ num }}</li></a>
{% endif %}
{% endfor %}
{% if current_page == last_page %}
<span class="pagination-link next-off"><li>></li></span>
{% else %}
<a href="{% url 'blog:index_with_tag' tag_id current_page|add:'+1' %}" class="pagination-link"><li>></li></a>
{% endif %}
</ul>
</div>
<!-- 選択されていない場合は通常のページ送り -->
{% else %}
<div class="pagination">
<ul>
{% if current_page == 1 %}
<span class="pagination-link prev-off"><li><</li></span>
{% else %}
<a href="{% url 'blog:index_with_page' current_page|add:'-1' %}" class="pagination-link"><li><</li></a>
{% endif %}
{% for num in num_page %}
{% if num == current_page %}
<span class="pagination-link is-active"><li>{{ num }}</li></span>
{% else %}
<a href="{% url 'blog:index_with_page' num %}" class="pagination-link"><li>{{ num }}</li></a>
{% endif %}
{% endfor %}
{% if current_page == last_page %}
<span class="pagination-link next-off"><li>></li></span>
{% else %}
<a href="{% url 'blog:index_with_page' current_page|add:'+1' %}" class="pagination-link"><li>></li></a>
{% endif %}
</ul>
</div>
{% endif %}
/* blog/static/css/style.css */
@charset "UTF-8";
...省略
/* --------------------------------
* 検索タグ
* -------------------------------- */
.tag-heading {
font-size: 1.6rem;
margin-bottom: 1rem;
color: #888;
letter-spacing: 1px;
}
.inline {
margin: 10px 0;
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
padding: 0;
}
.inline::-webkit-scrollbar {
width: 10px;
}
.inline::-webkit-scrollbar-track {
background: #fff;
border: none;
border-radius: 10px;
box-shadow: inset 0 0 2px #777;
}
.inline::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 10px;
box-shadow: none;
}
.tag-badge {
display: inline-block;
border-radius: 4px;
text-align: center;
border: solid 1px #ccc;
padding: 4px 16px;
cursor: pointer;
font-size: 1.4rem;
margin: 3px;
letter-spacing: 1.5px;
}
.tag-badge:hover {
background-color: #888;
color: #fff;
opacity: .6;
}
a.tag-active {
background-color: #888;
color: #fff;
}
...省略
全部で5つ記事があり、タグはこのような状況です。
microCMSが4つ、Pythonが2つ、Djangoが2つの記事ですね。
microCMSを選択した状態です。
4つあるので2ページ分の表示ですね。
ページャーの2をクリックするとmicroCMSの絞り込みが保持されたまま、ページ送りがされます。
Pythonは2つだけなので、1ページ分のみの表示です。
タグが増えても横スクロールで見られるようになります。
最低限の機能を備えたので、次回は静的サイト配信の準備をしていきます。