• トップ
  • ブログ一覧
  • 【第9回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【ブログトップを作る】
  • 【第9回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【ブログトップを作る】

    メディアチームメディアチーム
    2020.08.17

    IT技術

    第9回~

    連載「Python Responder+Firestoreでモダンでサーバーレスなブログシステムを作ってみる」第9回目です。

    前回は、「管理者ページの仕上げ」を行いました。

    featureImg2020.08.13【第8回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【管理者ページの仕上げ】第8回~モダンなフレームワークの使い方を学びながらブログシステムを構築~連載「Python Responder + F...

    ブログトップを作ろう

    今回は、「ユーザ (ブログ来訪者) が触れるビュー」を作っていきます。

    今までバックエンド部分が多かったのですが、フロント側もしっかり作っていきましょう!

    ブログトップページの構想を練る

    まずは、ブログのトップページに必要なものを考えてみましょう。

    1. 記事一覧 (必須!)
    2. ロゴとメニューバー (+各個別ページ)

    上記に加え、「ブログ本体のサムネ」もあれば、もっといいかもしれません。

    それでは、さっそく実装していきましょう!

    記事一覧を取得する

    はじめに、「記事一覧」を取得します。

    以前作ったdef get_articles(unreleased: bool) 関数を使って取得しましょう。

    コント―ラ修正

    そして、久々にルートのコントローラを修正していきます。

    1# controllers.py
    2@api.route('/')
    3def index(req, resp):
    4    articles = get_articles(unreleased=False)  # 公開済みのものだけ
    5
    6    resp.html = api.template('index.html',
    7                             title='',
    8                             articles=articles)

    ビューを書いて実行

    動作確認がてらビューを適当に書いて実行してみると、

    1<!--
    2templates/index.html
    3トップページ
    4-->
    5
    6{% extends "layout.html" %}
    7{% block content %}
    8
    9<br>
    10<h1>My Blog Name</h1>
    11<div class="main-container">
    12    <div class="article-main">
    13        {% for art in articles %}
    14        <p>{{ art['title'] }}</p>
    15        {% endfor %}
    16    </div>
    17</div>

    エラーが発生...??

    1google.api_core.exceptions.FailedPrecondition: 400 The query requires an index. You can create it here: https:// ******

    どうやら、クエリが上手く取得できていない様子。

    調べてみると、

    「並べ替えは同じフィールドで行う必要があります。」

    とのこと。

    つまり、「公開済みか否か」「最終更新日での並び替え」は別で行う必要があるのです。

    修正の上、ふたたび挑戦!

    そこで、以下のように修正してみました。

    1def get_articles(unreleased: bool = True):
    2    if unreleased:
    3        pass  # 省略
    4
    5    else:
    6        articles = [art.to_dict()
    7                    for art in
    8                    db.collection('articles').order_by(
    9                        'last_update',
    10                        direction=Query.DESCENDING
    11                    ).stream()
    12                    ]
    13        articles = [art for art in articles if art['released']]  # 公開済みのものだけにする
    14
    15    return articles

    これで、エラーもなく上手く動作しました!

    気を取り直して実装

    それでは、本格的に実装していきましょう。

    トップページでは、弊社ブログのように「記事を取得」して、「ボックス状に記事を陳列」させます。

    コントロ―ラ修正

    まずは、コントローラを少し修正します。

    1@api.route('/')
    2def index(req, resp):
    3    articles = get_articles(unreleased=False)  # 公開済みのものだけ
    4
    5    # サムネは別で保持
    6    md = markdown.Markdown()
    7    thumbnails = [
    8        md.convert(art['thumbnail']) for art in articles
    9    ]
    10
    11    resp.html = api.template('index.html',
    12                             title='',
    13                             thumbnails=thumbnails,
    14                             articles=articles)

    ビューをデザイン

    続いて、ビューをデザインしていきます。

    1<!--
    2templates/index.html
    3トップページ
    4-->
    5
    6{% extends "layout.html" %}
    7{% block content %}
    8
    9<br>
    10<h1>My Blog Name</h1>
    11<div class="main-container">
    12    <div class="top">
    13        <h2>ブログ一覧</h2>
    14        <div class="articles">
    15            {% for art in articles %}
    16            <a href="/category/{{art['category']}}/{{art['slug']}}">
    17                <div class="single">
    18                <p>
    19                    {% autoescape false %}
    20                    <div class="thumbnail">{{ thumbnails[loop.index - 1] }}</div>
    21                    {% endautoescape %}
    22                    {{ art['title'] }}<br>
    23                    <span class="category">{{ art['category'] }}</span>
    24                    <div class="info">{{ art['last_update'].strftime('%Y.%m.%d') }}: {{ art['author'] }}</div>
    25                </p>
    26            </div>
    27            </a>
    28            {% endfor %}
    29        </div>
    30    </div>
    31
    32</div>
    33
    34{% endblock %}
    1.top{
    2    width: 100%;
    3    margin: 2.0em 0;
    4}
    5.articles{
    6    width: 100%;
    7}
    8.articles .single{
    9    display: inline-block;
    10    vertical-align: top;
    11    width: 22.5%;
    12    height: 300px;
    13    color: #333333;
    14    border: 1px solid #333333;
    15    background-color: #fff;
    16    border-radius: 5px;
    17    padding: 10px 10px;
    18    margin: 5px 1px;
    19
    20}
    21.articles .single:hover{
    22    opacity: 0.7;
    23}
    24.article .single .thumbnail{
    25    text-align: center;
    26}
    27.articles .single .thumbnail img{
    28    height: 150px;
    29    width: 100%;
    30    margin: auto;
    31    object-fit: contain;
    32}
    33.articles .single .category{
    34    background-color: #2b52cd;
    35    color: #fff;
    36    border-radius: 3px;
    37    padding: 2px 5px;
    38}
    39.articles .single .info{
    40    font-size: smaller;
    41    color: #888888;
    42}

    完成!

    こんな感じになりました!

    適当に記事を追加して確認

    メインメニューの作成

    次に、「メインメニュー」を作っていきましょう。

    これは Python も Firestore も関係ないので、サクサクいきますよ!

    メインメニューの内訳

    今回、メインメニューには、「ロゴ」と以下の「リンクメニュー」を作成しようと思います。

    1. Top: トップリンク
    2. About: このブログについて
    3. Profile: 運営者について
    4. Twitter
    5. Contact: お問い合わせ

    なお、各個別ページの作成は割愛します。

    ロゴとメニューのレイアウトを決める

    「ロゴ」と「メインメニュー」を、一直線に並べるようなデザインにします。

    コード

    メニューはリストを使うので、コード自体は以下のような感じです。

    1<header>
    2    <div class="header">
    3        <a href="/" class="logo">
    4            <img src="/static/theme/logo.png" alt="My Blog Name">
    5        </a>
    6
    7        <div class="main-menu">
    8            <ul>
    9                <li><a href="/">Top</a></li>
    10                <li><a href="/about">About</a></li>
    11                <li><a href="/profile">Profile</a></li>
    12                <li><a href="https://twitter.com">Twitter</a></li>
    13                <li><a href="/contact">Contact</a></li>
    14            </ul>
    15        </div>
    16    </div>
    17</header>

    CSS

    CSSの方は、こんな感じです。

    最低限のシンプルなものなので、デザインはお好きなものを作ってみてくださいね。

    1header{
    2    width: 100%;
    3    height: 100px;
    4}
    5.header{
    6    display: flex;
    7    margin: 10px 10px;
    8}
    9.header .logo{
    10    display: inline-block;
    11    vertical-align: top;
    12    width: 25%;
    13    height: auto;
    14    margin: 0 0;
    15    padding: 0 10px;
    16}
    17.header .main-menu{
    18    width: 75%;
    19    margin: 0 0;
    20    text-align: center;
    21    display: inline-block;
    22    vertical-align: top;
    23}
    24.header .main-menu ul li{
    25    float: left;
    26    width: 15%;
    27    display: inline;
    28    background-color: #1e366a;
    29    color: #fff;
    30    border: 1px solid #1f3057;
    31}
    32.header .main-menu ul li:hover{
    33    opacity: 0.7;
    34}
    35.header .main-menu ul li a{
    36    display: block;
    37    color: #fff;
    38}

    見た目

    こんな感じになりました。

    ブログメインメニュー 

    筆者が即席で作ったロゴのクオリティは置いておいて、やはりロゴとメニューが揃うとブログっぽいですね!

    header.htmlとして共有する

    そうしたら、これを共有ファイルにして、「index.html」と「single.html」にインクルードするようにします。

    インクルードは{% include 'header.html' %} でOKです。

    記事ページもそれっぽくなった

    第10回へつづく!

    今回は、「ブログのトップページの整備」を終わらせました。

    残るは、「カテゴリページ」のみ!

    その他デザインも整える必要はありますが、機能としてはこれでラストになります。

    ラストスパート、頑張っていきましょう!

    次回の記事はこちら

    featureImg2020.08.20【第10回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【ブログ完成!!】第10回~モダンなフレームワークの使い方を学びながらブログシステムを構築~連載「Responder + Firesto...

    第1回はこちら

    featureImg2020.07.17【第1回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【初期セットアップ編】モダンなフレームワークの使い方を学びながらブログシステムを構築今回より、また新たに Python WebAPI 関連の...

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    featureImg2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background