「Django + microCMSでつくるブログサイト」シリーズの3番目の記事です。
今回はブログにサムネイル画像と記事のタグを設定していきます。
まずはmicroCMSの編集をしていきます。
記事ごとにタグを設定したいので、新しくタグAPI
を作りましょう。
post
APIの中にタグフィールドを用意してもいいのですが、microCMSには外部参照機能があるので、そちらを利用します。
のちのちのことを考えると、そうした方がタグで絞った検索等がやりやすいです。
microCMSの管理ページから、コンテンツ(API)
の右にある`+`ボタンをクリックするとAPIを追加できます。
上のように設定します。
単純にタグ名だけでもいいのですが、sortOrder
というフィールドも加えました。
この辺りは好みだとは思いますが、例えばDjango
,Python
というタグを振っていた場合、`Python`が先にあった方がすっきりします。
そのあたりの順番を設定できるようにフィールドに情報を持たせておきます。
詳細設定
をクリックすると、投稿者が分かりやすいように説明文を追記できます。こういうかゆい所に手が届く機能が本当にありがたいです…
登録が完了したら、タグを追加していきます。
Python
は一番最初に表示したいので、順番は0
です。
Django
、microCMS
も追加しました。
順番は全て1です。
post
APIのAPI設定からフィールドを追加していきます。
thumbnail
と`tag`ですね。
tag
の種類は複数コンテンツ参照
を選択します。
さきほど作成したtag
APIを選択します。
記事の投稿画面に行きますと、サムネイルとタグが設定できるようになります。
タグの追加する
をクリックすると、さきほど作成したタグが一覧で表示されますので、クリックして追加できます。
左上のほうをみると、検索する機能もあります。
こういったタグはどんどん増えていきますので、検索機能がデフォルトであるのはうれしいですね。
適当な画像をアップロードし、タグも全て追加しました。
その他は適当に埋めて公開します。
これでmicroCMS側の設定は終了です。
フィールドを追加した結果、以下のように一覧が返ってきます。
一度確認しておきましょう。
[{'createdAt': '2021-08-07T00:23:42.043Z',
'description': 'サムネイルとタグの挙動をテストします。',
'id': 'thumbnail-and-tag-test',
'keywords': 'microCMS サムネイル',
'publishedAt': '2021-08-07T00:30:55.419Z',
'revisedAt': '2021-08-07T00:30:55.419Z',
'tag': [{'createdAt': '2021-08-07T00:19:52.757Z',
'id': 'yg4-8dn8d-n9',
'name': 'microCMS',
'publishedAt': '2021-08-07T00:19:52.757Z',
'revisedAt': '2021-08-07T00:19:52.757Z',
'sortOrder': 1,
'updatedAt': '2021-08-07T00:19:52.757Z'},
{'createdAt': '2021-08-07T00:19:34.205Z',
'id': 'ingpdu3m9gc',
'name': 'Django',
'publishedAt': '2021-08-07T00:19:34.205Z',
'revisedAt': '2021-08-07T00:19:34.205Z',
'sortOrder': 1,
'updatedAt': '2021-08-07T00:19:34.205Z'},
{'createdAt': '2021-08-07T00:11:55.642Z',
'id': 'vt247rw4jt',
'name': 'Python',
'publishedAt': '2021-08-07T00:11:55.642Z',
'revisedAt': '2021-08-07T00:19:21.482Z',
'sortOrder': 0,
'updatedAt': '2021-08-07T00:19:21.482Z'}],
'text': '<h2 '
'id="h6f9634bf3b">この記事の内容</h2><p>サムネイル画像を設定しました。<br>タグを設定しました。<br></p>',
'thumbnail': {'height': 540,
'url': 'https://images.microcms-assets.io/assets/24593cd102554657b424fa2ebe3589bd/b7dd1db75af948428a218f621b690e66/image1.jpg',
'width': 959},
'title': 'サムネイルとタグのテスト',
'updatedAt': '2021-08-07T00:30:55.419Z'},
...省略 次の記事が続く
]
次にDjango内のファイルを編集していきます。
index.html
を以下のようにします。
<!-- blog/templates/blog/index.html -->
{% extends "blog/base.html" %}
{% load blog %}
{% load static %}
{% block content %}
<div class="container" style="padding-top:112px;">
{% for post in post_list %}
<article class="article">
<div class="thumbnail">
<a href="{% url 'blog:post_detail' post.id %}">
{% if post.thumbnail %}
<img class="post-thumbnail" src="{{ post.thumbnail.url }}" alt="{{ post.title }}">
{% else %}
<img class="post-thumbnail" src="{% static 'images/noimage.png' %}" alt="{{ post.title }}">
{% endif %}
</a>
</div>
<div class="detail">
<p class="created-at">{{ post.createdAt | date_from_isoformat }}</p>
<a href="{% url 'blog:post_detail' post.id %}">
<h1 class="post-title" style="margin-top:1rem;">{{ post.title }}</h1>
</a>
<p class="post-description">{{ post.description | linebreaks }}</p>
{% if post.tag %}
<div class="tag">
<span class="post-tag">Tags:</span>
{% for tag in post.tag %}
<span class="post-tag" style="margin-left:3px;">{{ tag.name }}</span>
{% if not forloop.last %}
<span style="margin:0 3px; color:#888">/</span>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
</article>
{% endfor %}
</div>
{% endblock %}
まず、サムネイルの部分ですが、記事にサムネイルを設定しないということもあると思うので、if文で分岐させています。
<div class="thumbnail">
<a href="{% url 'blog:post_detail' post.id %}">
{% if post.thumbnail %}
<img class="post-thumbnail" src="{{ post.thumbnail.url }}" alt="{{ post.title }}">
{% else %}
<img class="post-thumbnail" src="{% static 'images/noimage.png' %}" alt="{{ post.title }}">
{% endif %}
</a>
</div>
static
配下にimage
ディレクトリを作り、noimage.png
という名前で、サムネイルがない場合の表示画像を保存しています。
適当にネットでno image
と検索すれば拾えると思います。
次にタグの部分は以下のようになっています。
{% if post.tag %}
<div class="tag">
<span class="post-tag">Tags:</span>
{% for tag in post.tag %}
<span class="post-tag" style="margin-left:3px;">{{ tag.name }}</span>
{% if not forloop.last %}
<span style="margin:0 3px; color:#888">/</span>
{% endif %}
{% endfor %}
</div>
{% endif %}
これも同様にif文で分岐させています。
あとは順番に表示させるだけですね。
{% if not forloop.last %}
以下ブロックは、区切り文字の制御のためです。
何もしないと、DjangoPythonmicroCMS
のように表示されてしまうので、間に/
を入れています。
ただし Django / Python / microCMS /
という風に最後にスラッシュが入るとかっこわるいです。
なので、if文で繰り返しの最後以外の時はスラッシュを入れる、という風にしています。
ここまででこのように表示されるようになります。
よくある画像を左、記事タイトルを右に配置するレイアウトです。
一覧ページのhtmlのブロックをまとめると、下記のようになっています。
<article class="article">
<div class="thumbnail">
画像
</div>
<div class="detail">
日付
タイトル
<div class="tag">
タグ
</div>
</div>
</article>
画像ブロックと記事ブロックを横並びにし、記事ブロックの中のタグブロックをボトムに配置、という形にします。
/* blog/static/css/style.css */
@charset "UTF-8";
...省略
/* --------------------------------
* 記事一覧
* -------------------------------- */
.article {
margin-bottom: 6rem;
width: 100%;
/*追加 横並びの設定*/
display: flex;
justify-content: space-between;
}
/* 追加 articleの40% */
.thumbnail {
width: 40%;
}
/* 追加 articleの55% */
.detail {
width: 55%;
position: relative;
}
/* 追加 タグは一番下 */
.tag {
position: absolute;
bottom: 0;
}
.created-at {
font-size: 1.4rem;
color: 888;
}
.post-title {
font-size: 2.0rem;
color: #0d1a3c;
line-height: 1.6;
letter-spacing: 1px;
}
/* 追加 タグのスタイル */
.post-tag {
font-size: 1.2rem;
color: 888;
opacity: 0.7;
letter-spacing: 1px;
}
/*追加 記事詳細のスタイル*/
.post-description {
margin-top: 1rem;
font-size: 1.4rem;
color: #0d1a3c;
line-height: 1.6;
letter-spacing: 1px;
}
/* 追加 画像のスタイル*/
.post-thumbnail {
width: 100%;
height: auto;
}
...省略
ここまでで以下のような表示になります。
レスポンシブ対応も行っておきましょう。
画像幅が狭まったときに画像と文字が縦に並ぶようにします。
/* 追加 */
/* 完全に縦に並べる */
@media(max-width:768px) {
.article {
flex-direction: column;
}
.thumbnail {
width: 100%;
}
.detail
{
width: 100%;
margin-top: 10px;
padding-left: 0;
}
.tag {
margin-top: 10px;
position: relative;
}
}
/* 横並びだけど、記事部分がせまくなる。
そのため、文字が被る可能性があるので、タグ部分のbottom配置を解除 */
@media(max-width:992px) {
.tag {
margin-top: 10px;
position: relative;
}
}
768px以下は完全に縦に並べます。
768px以上、992px以下は横並びだけど、タグの部分のボトム配置を解除します。
相対的にスペースがせまくなるので、例えば、タイトルや詳細文が長い記事だと、tagの部分が文字被りを起こす可能性があるためです。
現状はmicroCMS
,Django
,Python
の順で並んでいます。
これをタグのAPIを作るときに設定したsortOrder
順に並び変えたいです。
ついでにABC順で並んだ方がいいでしょう。
関係のない部分を端折りますが、postのtagの中身はこのような構成になっています。
[
{
'name': 'microCMS',
'sortOrder': 1
},
{
'name': 'Django',
'sortOrder': 1
},
{
'name': 'Python',
'sortOrder': 0
}
]
辞書のリストなので、key引数
を用いたsort関数で並び替えます。
このようなケースではフィルターを自作するのが最短でしょう。
blog.py
を以下のように編集します。
# blog/templatetags/blog.py
from django import template
import datetime
register = template.Library()
...省略
# 追加
@register.filter
def sorted_post_tag(post_tag):
"""ポストに紐づいたタグを並び替える"""
post_tag.sort(key=lambda x: (x['sortOrder'], x['name']))
return post_tag
今回のように複数指定する際はkey=lambda x: (x['sortOrder'], x['name'])
というようにタプルで指定します。
後ろから評価されるので、名前順→sortOrder順で並び替えられます。
次にindex.html
の編集です。
<div class="tag">
<span class="post-tag">Tags:</span>
<!-- 作成したフィルターを呼び出す -->
{% for tag in post.tag|sorted_post_tag %}
<span class="post-tag" style="margin-left:3px;">{{ tag.name }}</span>
{% if not forloop.last %}
<span style="margin:0 3px; color:#888">/</span>
{% endif %}
{% endfor %}
</div>
意図している通りに並びました。
詳細ページにも画像とタグを表示させます。
ここは新しいことはないです。
<!-- blog/templates/blog/post_detail.html -->
{% extends 'blog/base.html' %}
{% load blog %}
{% load static %}
{% block meta_title %}{{ post.title }} - {{ block.super }}{% endblock %}
{% block meta_description %}{{ post.description }}{% endblock %}
{% block meta_keywords %}{{ post.keywords }}{% endblock %}
{% block content %}
<div class="container" style="padding-top:112px;">
<article class="post">
<!-- 追加 サムネイル-->
{% if post.thumbnail %}
<img class="post-thumbnail" src="{{ post.thumbnail.url }}" alt="{{ post.title }}" style="margin-bottom:20px;">
{% endif %}
<!-- 追加 タグ -->
{% if post.tag %}
<div style="margin-bottom:10px;">
{% for tag in post.tag|sorted_post_tag %}
<span class="post-tag" style="margin-left:3px;">{{ tag.name }}</span>
{% if not forloop.last %}
<span style="margin:0 3px; color:#888">/</span>
{% endif %}
{% endfor %}
</div>
{% endif %}
<p class="created-at">{{ post.createdAt | date_from_isoformat }}</p>
<h1 class="post-title">
{{ post.title }}
</h1>
<div class="markdown-body">
{% autoescape off %}
{{ post.text }}
{% endautoescape %}
</div>
</article>
</div>
{% endblock %}
{% block extrajs %}
<!-- コードハイライト -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/styles/hybrid.min.css">
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.16.2/build/highlight.min.js"></script>
<script type="text/javascript">
hljs.initHighlightingOnLoad();
</script>
{% endblock %}