Django + microCMSでつくるブログサイト ①記事一覧ページの作成

「Django + microCMSでつくるブログサイト」の最初の記事です。
今回は環境設定から記事一覧ページの作成までをまとめます。

microCMSの設定

まずはmicroCMS側の設定をしていきます。
登録がまだの方は公式ブログを参照するとわかりやすいです。この記事ではサービス情報の登録から説明をしていきます。
microCMSのはじめ方
登録が完了しましたら、サービスを作成します。
とりあえず以下のようにしました。



サービスIDは他の人とは重複して使用できないので、何か違うものを入力してください。

次にサービス画像を選択という画面が出てきますが、こちらは飛ばして大丈夫です。
その後、料金プランの選択画面が出ます、無料のhobbyプランを選択しましょう。

進めていくとAPIの基本情報を入力する画面が出てきます。



今回はブログを作成するので、API名を記事、エンドポイントをpostと入力しました。



リスト形式を選択します。

APIスキーマの入力画面に移ります。



ここで、ブログにどんな情報を持たせたいか?ということを定義していきます。



とりあえず、タイトルと本文を設定します。 こちらの設定は後から追加・変更が簡単にできます。
本文に割り当てたリッチエディタはマークダウン記法に対応しています。 Djangoモデルでブログサイトを作る場合、マークダウン記法に対応させるのが少し手順が多いので、うれしいポイントです。

適当な記事を書いてみる




管理画面の右上にある追加ボタンを押すと、記事が追加できます。



コンテンツIDは初期表示でランダム文字列が割り振られるので、以下のように変えました。

公開を押すと投稿がされます。



トップページに戻ると、きちんと公開されていることが分かります。

APIキーの確認

Django側での記事の取得に必要なAPIキーを確認していきます。
管理画面の右上のAPI設定をクリックします。

遷移したページでAPIリファレンスと続けます。

次の画面でX-API-KEYが表示されているかと思います。

こちらをのちに使うので、メモしておきましょう。
また、リクエストURLの共通部分はhttps://your-service-id.microcms.io/api/v1となります。

私の場合はdjango-microcmsと入力していたので、https://django-microcms.microcms.io/api/v1ですね。
こちらものちに必要になりますので、メモしておきす。

djangoプロジェクトを立ち上げる

次にdjango側の設定をしていきます。 適当なフォルダを作り、仮想環境をactivateします。

mkdir django-microcms
cd django-microcms
python -m venv myvenv
myvenv\\scripts\\activate


djangorequestsをpipでinstallします。

pip install django
pip install requests


djangoのプロジェクトとアプリをスタートしましょう。 最後の.を忘れないようにします。

django-admin startproject project .
python manage.py startapp blog


とりあえず組み込みモデルをmigrateして、superuserも作っておきましょう。

python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  ...省略

python manage.py createsuperuser


ここまでで、コマンドラインは終わりです。 次にソースコードを書いていきます。

projectの編集

settings.pyを以下のように編集します。

# project/settings.py

...省略

INSTALLED_APPS = [
    'blog.apps.BlogConfig', #追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

...省略

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

...省略

STATIC_URL = '/static/'
#追加
STATIC_ROOT = Path(BASE_DIR, 'static')
API_KEY='your api key'
BASE_URL='your microcms request url'


先ほど保存したAPIキーとリクエストURLをsettings.py内に書いておきましょう。
次にurls.pyです。

#project/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls'))
]


アプリ内ファイルの編集

まずはアプリ内のurls.pyにurlパターンを定義します。 現状は記事一覧のみです。下のようにします。

#blog/urls.py

from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.post_list, name='index'),
]


次にviews.pyの編集します。
ここでrequestsライブラリを使用して、microCMSで先ほど作成した記事を呼び出す、という流れです。
microCMSのコンテンツはヘッダーを{'X-API-KEY':apiキー}として、GETリクエストを送ると取得できます。
ためしにコマンドラインに取得情報を表示させてみましょう。

#blog/views.py

from django.shortcuts import render
import requests
from django.conf import settings

def post_list(request):
    """記事一覧"""
    end_point =

 '/post'
    url = getattr(settings, "BASE_URL", None)
    api_key = getattr(settings, "API_KEY", None)
    headers = {'X-API-KEY': api_key}
    res = requests.request('GET', url=url + end_point, headers=headers)
    from pprint import pprint
    pprint(res.json())
    


ローカルサーバーを起動させます。

python manage.py runserver


http://127.0.0.1:8000/にアクセスすると、エラー画面が出ると思いますが、コマンドラインに先ほど書いた記事がプリントされます。

{'contents': [{'createdAt': '2021-08-04T13:22:29.259Z',
               'id': 'my-first-blog',
               'publishedAt': '2021-08-04T13:22:29.259Z',
               'revisedAt': '2021-08-04T13:22:29.259Z',
               'text': '<h2 '
                       'id="hfd9fd14e3d">最初のブログです</h2><p>microcmsを使った最初のブログです。<br>いろいろなことを 書いていきます。</p>',
               'title': '最初のブログです',
               'updatedAt': '2021-08-04T13:22:29.259Z'}],
 'limit': 10,
 'offset': 0,
 'totalCount': 1}


記事の内容が取得できていることがわかります。 あとはcontextにcontents以下を渡して表示させるという流れです。 以下のように書き換えます。

#blog/views.py

...省略

def post_list(request):
    """記事一覧"""
	...省略
    res = requests.request('GET', url=url + end_point, headers=headers)
    context = {
        'post_list': res.json()['contents']
    }

    return render(request, 'blog/index.html', context)


次に表示させるhtmlファイルを作成していきます。 blogディレクトリ以下にtemplates、そのなかにblogディレクトリを作成します。
base.htmlを作っておきましょう。

<!-- blog/templates/blog/base.html -->

{% load static %}

<!doctype html>
<html lang="ja">

<head>
  <!-- Required meta tags -->
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Django microCMS Blog</title>

</head>

<body>
  {% block content %}{% endblock %}
</body>

</html>


次にこちらを読み込む形で、記事一覧を表示するindex.htmlを作成します。

<!-- blog/templates/blog/index.html -->

{% extends "blog/base.html" %}
{% load static %}
{% block content %}

  {% for post in post_list  %}
  

<article class="article">
    <p>{{ post.createdAt }}</p>
    <h1>{{ post.title }}</h1>
  </article>
  {% endfor %}

</div>
{% endblock %}




装飾をしていないので、非常に味気ないですが、とりあえずは表示できました。
microCMSから二つ目の記事を追加してみましょう。 内容はなんでも大丈夫です。



そうすると、以下のように一覧画面に記事が追加されます。

装飾をする


このままだとタイトルと日付だけなので、簡単な装飾を施していきます。


ヘッダーの作成

blog/templates配下にcomponentsフォルダを作り、layout-header.htmlを作成します。

<!-- blog/templates/components/layout-header.html -->
<header class="header">
  <h1 class="">
    <a href="{% url 'blog:index' %}" class="page-title">Django microCMS Blog</a>
  </h1>
  <nav class="nav">
    <ul class="main-nav">
      <li><a href="{% url 'blog:index' %}">HOME</a></li>
    </ul>
  </nav>
</header>


base.htmlの編集


次に作成したヘッダーを読み込みます。 ついでにこれから作るcssの読み込みの記述も行っておきましょう。

<!-- blog/templates/blog/base.html -->

{% load static %}

<!doctype html>
<html lang="ja">

<head>
  <!-- 追加 -->
  <link rel="stylesheet" href="{% static "css/reset.css" %}">
  <link rel="stylesheet" href="{% static "css/style.css" %}">
</head>

<body>
  <!-- ヘッダーの読み込み -->
  {% include "blog/components/layout-header.html" %}
  {% block content %}{% endblock %}
</body>

</html>


日付表記の変更

microCMSからの日付データはISO 8601形式のUTC(協定世界時)にて返却されます。
これをyyyy-mm-dd形式に変えたいです。 色々方法はあると思うのですが、自作フィルタを作成して対応します。
temlatesフォルダにtemplatetagsディレクトリを作り、blog.pyを作成します。
協定世界時は日本時間から9時間前なので、一旦datetime型に直してから、9時間分足します。

from django import template
import datetime

register = template.Library()

@register.filter
def date_from_isoformat(date_string):
    """UTC時刻をyyyy-mm-ddに置き換える"""
    d = datetime.datetime.fromisoformat(date_string[:-1])
    d += datetime.timedelta(hours=9)
    return datetime.datetime.strftime(d, "%Y-%m-%d")


次に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">
      <p class="created-at">{{ post.createdAt | date_from_isoformat }}</p>
      <h1 class="post-title" style="margin-top:1rem;">{{ post.title }}</h1>
    </article>
  {% endfor %}
</div>
{% endblock %}


cssで装飾

最後にcssで装飾していきます。
blog配下にstatic/cssというディレクトリを作りcssファイルを作成します。
reset.cssstyle.cssです。

/* blog/static/css/reset.css */

/**
 * html5doctor.com Reset Stylesheet v1.6.1 (<http://html5doctor.com/html-5-reset-stylesheet/>)
 * Richard Clark (<http://richclarkdesign.com>)
 * <http://cssreset.com>
 */
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
    margin:0;
    padding:0;
    border:0;
    outline:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}
body {
    line-height:1;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
    display:block;
}
nav ul {
    list-style:none;
}
blockquote, q {
    quotes:none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content:'';
    content:none;
}
a {
    margin:0;
    padding:0;
    font-size:100%;
    vertical-align:baseline;
    background:transparent;
}
/* change colours to suit your needs */
ins {
    background-color:#ff9;
    color:#000;
    text-decoration:none;
}
/* change colours to suit your needs */
mark {
    background-color:#ff9;
    co

lor:#000;
    font-style:italic;
    font-weight:bold;
}
del {
    text-decoration: line-through;
}
abbr[title], dfn[title] {
    border-bottom:1px dotted;
    cursor:help;
}
table {
    border-collapse:collapse;
    border-spacing:0;
}
/* change border colour to suit your needs */
hr {
    display:block;
    height:1px;
    border:0;
    border-top:1px solid #cccccc;
    margin:1em 0;
    padding:0;
}
input, select {
    vertical-align:middle;
}
/* blog/static/css/style.css */

@charset "UTF-8";

/* --------------------------------
 * base
 * -------------------------------- */
html {
  font-size: 62.5%;
}

body {
  color: #333;
  font-size: 1.2rem;
  font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}

*, *::before, *::after {
  box-sizing: border-box;
}

a:link, a:visited, a:hover, a:active {
  color: #333;
  text-decoration: none;
}

/* --------------------------------
 * ヘッダー
 * -------------------------------- */
.header {
  padding: 30px 4% 10px;
  position: fixed;
  top: 0;
  width: 100%;
  background-color: #fff;
  display: flex;
  align-items: center;
  z-index: 10;
}

a.page-title {
  font-size: 2rem;
  margin: 0;
  padding: 0;
  color: #888;
}

.main-nav {
  list-style: none;
  margin: 0;
  display: flex;
}

.main-nav li {
  margin: 0 0 0 15px;
  font-size: 14px;
}

.main-nav a {
  color: #888;
}

.main-nav a:hover {
  opacity: 0.6;
}

/* --------------------------------
 * コンテナ
 * -------------------------------- */
.container {
  position: relative;
  width: 100%;
  margin: 0 auto 0;
  color: #0d1a3c;
  padding-right: 4%;
  padding-left: 4%;
}

@media (min-width: 576px) {
  .container {
    max-width: 540px;
  }
}

@media (min-width: 768px) {
  .container {
    max-width: 760px;
  }
}

@media (min-width: 992px) {
  .container {
    max-width: 960px;
  }
}

/* --------------------------------
 * 記事一覧
 *

 -------------------------------- */
.article {
  margin-bottom: 6rem;
  width: 100%;
}

.created-at {
  font-size: 1.4rem;
  color: 888;
}

.post-title {
  font-size: 2.0rem;
  color: #0d1a3c;
  line-height: 1.6;
  letter-spacing: 1px;
}

ここまでで以下のような見た目になりました。 多少ブログっぽくなったと思います。



次回は記事詳細ページを作っていきます。

TOPページ