初心者向け : Flaskログイン機能をつけてQAサイトを作る 2 -Bootstrap+UI修正-

「初心者向け : Flaskログイン機能をつけてQAサイトを作る 2 -Bootstrap+UI修正-」のアイキャッチ画像

 

テンプレートのダウンロード

今回はこちらのテンプレートを使用します
こちらのリンクの「Free Download」をクリックし、
テンプレートをダウンロードします
https://startbootstrap.com/themes/freelancer/

ダウンロード後はたくさんフォルダとファイルがありますが、
とりあえず必要なファイルはこちらだけです

 

templates/index.htmlへ適用する

先ほどダウンロードしたテンプレートのhtmlを
templates/index.htmlに貼り付けます

ちなみに今回作成しているFlaskのプロジェクトではblock contentの中に
htmlを入れてあげることで反映させることができます

{% extends "layout.html" %}

{% block content %}
    <h1>
        Index page
    </h1>
{% endblock %}

 

貼り付け後はこのようなhtmlとなります

 

{% extends "layout.html" %}

{% block content %}
    <!DOCTYPE html>
    <html lang="en">

    <head>

        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="description" content="">
        <meta name="author" content="">

        <title>Heroic Features - Start Bootstrap Template</title>

        <!-- Bootstrap core CSS -->
        <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

        <!-- Custom styles for this template -->
        <link href="css/heroic-features.css" rel="stylesheet">

    </head>

    <body>

    <!-- Navigation -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
        <div class="container">
            <a class="navbar-brand" href="#">Start Bootstrap</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                    aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarResponsive">
                <ul class="navbar-nav ml-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="#">Home
                            <span class="sr-only">(current)</span>
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">About</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Services</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#">Contact</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- Page Content -->
    <div class="container">

        <!-- Jumbotron Header -->
        <header class="jumbotron my-4">
            <h1 class="display-3">A Warm Welcome!</h1>
            <p class="lead">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa, ipsam, eligendi, in quo sunt
                possimus non incidunt odit vero aliquid similique quaerat nam nobis illo aspernatur vitae fugiat numquam
                repellat.</p>
            <a href="#" class="btn btn-primary btn-lg">Call to action!</a>
        </header>

        <!-- Page Features -->
        <div class="row text-center">

            <div class="col-lg-3 col-md-6 mb-4">
                <div class="card h-100">
                    <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                    <div class="card-body">
                        <h4 class="card-title">Card title</h4>
                        <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente esse
                            necessitatibus neque.</p>
                    </div>
                    <div class="card-footer">
                        <a href="#" class="btn btn-primary">Find Out More!</a>
                    </div>
                </div>
            </div>

            <div class="col-lg-3 col-md-6 mb-4">
                <div class="card h-100">
                    <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                    <div class="card-body">
                        <h4 class="card-title">Card title</h4>
                        <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo magni
                            sapiente, tempore debitis beatae culpa natus architecto.</p>
                    </div>
                    <div class="card-footer">
                        <a href="#" class="btn btn-primary">Find Out More!</a>
                    </div>
                </div>
            </div>

            <div class="col-lg-3 col-md-6 mb-4">
                <div class="card h-100">
                    <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                    <div class="card-body">
                        <h4 class="card-title">Card title</h4>
                        <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente esse
                            necessitatibus neque.</p>
                    </div>
                    <div class="card-footer">
                        <a href="#" class="btn btn-primary">Find Out More!</a>
                    </div>
                </div>
            </div>

            <div class="col-lg-3 col-md-6 mb-4">
                <div class="card h-100">
                    <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                    <div class="card-body">
                        <h4 class="card-title">Card title</h4>
                        <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo magni
                            sapiente, tempore debitis beatae culpa natus architecto.</p>
                    </div>
                    <div class="card-footer">
                        <a href="#" class="btn btn-primary">Find Out More!</a>
                    </div>
                </div>
            </div>

        </div>
        <!-- /.row -->

    </div>
    <!-- /.container -->

    <!-- Footer -->
    <footer class="py-5 bg-dark">
        <div class="container">
            <p class="m-0 text-center text-white">Copyright &copy; Your Website 2019</p>
        </div>
        <!-- /.container -->
    </footer>

    <!-- Bootstrap core JavaScript -->
    <script src="vendor/jquery/jquery.min.js"></script>
    <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

    </body>

    </html>

{% endblock %}

 

localhost:5000で確認すると
画像やcssがないので見た目も綺麗ではありません

※開発者ツールがわからないという方はこちらから調べてください
Web開発でよく使う、特に使えるChromeデベロッパー・ツールの機能

 

なので、次はcssを追加していきます

 

FlaskにCSSを追加する

FlaskにCSSを追加するにはstaticディレクトリを作成する必要があります
さらにその中にcssとjsディレクトリを作成します

ダウンロードしたテンプレートのフォルダに入っている

  • vendor/bootstrap/cssの中身全部をstatic/cssへ
  • vendor/bootstrap/jsの中身全部をstatic/jsへ
  • css/heroic-features.cssをstatic/cssへ

ペーストする

このような内容となります
開発環境の違いで表示のされ方が異なるかもしれません

 

追加が完了したらbootstrapを読み込ませるために
templates/layout.htmlをこのように修正します

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {# bootstrapの読み込み #}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-grid.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-reboot.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/heroic-features.css') }}">


    <title>QA-Site</title>
</head>

<body>
<ul>
    {# ログインしていれば表示 #}
    {% if  current_user.is_authenticated %}
        <li><a href="{{ url_for('index') }}">Index</a></li>
        {#ここに追加#}
        <li><a href="{{ url_for('questions.find_all') }}">Questions</a></li>
        <li><a href="{{ url_for('auth.logout') }}">ログアウト</a></li>
    {% endif %}

    {#ログインしていなければ表示#}
    {% if not current_user.is_authenticated %}
        <li><a href="{{ url_for('auth.signup') }}">新規登録</a></li>
        <li><a href="{{ url_for('auth.login') }}">ログイン</a></li>
    {% endif %}
</ul>
{# flaskのメッセージを表示 #}
{% with messages = get_flashed_messages() %}
    {% if messages %}
        <div>
            {{ messages[0] }}
        </div>
    {% endif %}
{% endwith %}
{% block content %}
{% endblock %}


{# bootstrapの読み込み #}
<script type=text/javascript src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script type=text/javascript src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>

</body>

</html>

 

その後flaskを再起動してlocalhost:5000で確認しましょう

 

先ほどよりかはだいぶ前進しました!

 

ルーティングやヘッダーを修正する

先ほどはtemplates/index.htmlにbootstrapのhtmlを追加しましたが、
今からlayout.htmlに移動してヘッダーに

  • ログインしていれば「Questions」のリンクを表示

リンクを追加します。

修正後はtemplates/layout.htmlはこのようになります

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {# bootstrapの読み込み #}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-grid.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-reboot.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/heroic-features.css') }}">
    <title>QA-Site</title>
</head>

<body>


<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container">
        <a class="navbar-brand" href="#">Start Bootstrap</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="{{ url_for('index') }}">
                        Home
                        <span class="sr-only">(current)</span>
                    </a>
                </li>
                {% if current_user.is_authenticated %}
                    {#ここに追加#}
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('questions.find_all') }}">Questions</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.logout') }}">ログアウト</a>
                    </li>
                {% endif %}
                {#ログインしていなければ表示#}
                {% if not current_user.is_authenticated %}
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.signup') }}">新規登録</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.login') }}">ログイン</a>
                    </li>
                {% endif %}
            </ul>
        </div>
    </div>
</nav>

<!-- Page Content -->
<div class="container">
    {# flaskのメッセージを表示 #}
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <div>
                {{ messages[0] }}
            </div>
        {% endif %}
    {% endwith %}
    {% block content %}
    {% endblock %}


    <!-- Jumbotron Header -->
    <header class="jumbotron my-4">
        <h1 class="display-3">A Warm Welcome!</h1>
        <p class="lead">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa, ipsam, eligendi, in quo sunt
            possimus non incidunt odit vero aliquid similique quaerat nam nobis illo aspernatur vitae fugiat numquam
            repellat.</p>
        <a href="#" class="btn btn-primary btn-lg">Call to action!</a>
    </header>

    <!-- Page Features -->
    <div class="row text-center">

        <div class="col-lg-3 col-md-6 mb-4">
            <div class="card h-100">
                <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                <div class="card-body">
                    <h4 class="card-title">Card title</h4>
                    <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente esse
                        necessitatibus neque.</p>
                </div>
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">Find Out More!</a>
                </div>
            </div>
        </div>

        <div class="col-lg-3 col-md-6 mb-4">
            <div class="card h-100">
                <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                <div class="card-body">
                    <h4 class="card-title">Card title</h4>
                    <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo magni
                        sapiente, tempore debitis beatae culpa natus architecto.</p>
                </div>
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">Find Out More!</a>
                </div>
            </div>
        </div>

        <div class="col-lg-3 col-md-6 mb-4">
            <div class="card h-100">
                <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                <div class="card-body">
                    <h4 class="card-title">Card title</h4>
                    <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente esse
                        necessitatibus neque.</p>
                </div>
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">Find Out More!</a>
                </div>
            </div>
        </div>

        <div class="col-lg-3 col-md-6 mb-4">
            <div class="card h-100">
                <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                <div class="card-body">
                    <h4 class="card-title">Card title</h4>
                    <p class="card-text">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo magni
                        sapiente, tempore debitis beatae culpa natus architecto.</p>
                </div>
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">Find Out More!</a>
                </div>
            </div>
        </div>

    </div>
    <!-- /.row -->

</div>
<!-- /.container -->

<!-- Footer -->
<footer class="py-5 bg-dark">
    <div class="container">
        <p class="m-0 text-center text-white">Copyright &copy; Your Website 2019</p>
    </div>
    <!-- /.container -->
</footer>


{# bootstrapの読み込み #}
<script type=text/javascript src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script type=text/javascript src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>

</body>

</html>

 

templates/index.htmlは前の状態にひとまず戻します

{% extends "layout.html" %}

{% block content %}
    <h1>
        Index page
    </h1>
{% endblock %}

 

この状態で、ログインしたりログアウトしてテストを行いましょう

ちなみにlayout.htmlへ直接貼り付けたので全ての要素がこのlayoutを反映していることになってます

これでどの画面からでもログインしたり、ログアウトしたりすることができます!

Questionsをリスト表示する

templates/questions/index.htmlの綺麗に表示されるようにしていきます
templates/questions/index.html

{% extends "layout.html" %}

{% block content %}
    <div class="container">
        <header class="jumbotron my-4">
            <h1 class="display-3">Questions</h1>
            <a class="btn btn-primary btn-lg" href="{{ url_for('questions.add') }}">質問する!</a>
        </header>
        <div class="row text-center">
            {% for question in quesions %}
                <div class="col-lg-3 col-md-6 mb-4">
                    <div class="card h-100">
                        <img class="card-img-top" src="http://placehold.it/500x325" alt="">
                        <div class="card-body">
                            <h4 class="card-title">{{ question.title }}</h4>
                            <p class="card-text">{{ question.body }}</p>
                        </div>
                        <div class="card-footer">
                            <a class="btn btn-primary" href="{{ url_for('questions.find_one', question_id=question.id) }}">詳細</a>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
    </div>
{% endblock %}

 

localhost:5000/questionsではこのように表示されています

 

ただし下の方にスクロールするとtemplates/layout.htmlのhtmlも表示されて
おかしなことになっているので、templates/layout.htmlを修正します

templates/layout.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {# bootstrapの読み込み #}
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-grid.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap-reboot.css') }}">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/heroic-features.css') }}">
    <title>QA-Site</title>
</head>

<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
    <div class="container">
        <a class="navbar-brand" href="#">Start Bootstrap</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav ml-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="{{ url_for('index') }}">
                        Home
                        <span class="sr-only">(current)</span>
                    </a>
                </li>
                {% if current_user.is_authenticated %}
                    {#ここに追加#}
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('questions.find_all') }}">Questions</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.logout') }}">ログアウト</a>
                    </li>
                {% endif %}
                {#ログインしていなければ表示#}
                {% if not current_user.is_authenticated %}
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.signup') }}">新規登録</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('auth.login') }}">ログイン</a>
                    </li>
                {% endif %}
            </ul>
        </div>
    </div>
</nav>
{# flaskのメッセージを表示 #}
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <div>
                {{ messages[0] }}
            </div>
        {% endif %}
    {% endwith %}
    {% block content %}
    {% endblock %}

<!-- Footer -->
<footer class="py-5 bg-dark">
    <div class="container">
        <p class="m-0 text-center text-white">Copyright &copy; Your Website 2019</p>
    </div>
    <!-- /.container -->
</footer>

{# bootstrapの読み込み #}
<script type=text/javascript src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script type=text/javascript src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>

</body>

</html>

 

修正後はlayout.htmlの余分なhtmlを削除したので綺麗になっています

 

 

Questionsの詳細を修正する

Questionsの一覧は綺麗にしたので、次は詳細画面を綺麗にしていきます

templates/questions/show.htmlをこのように修正します

 

{% extends "layout.html" %}

{% block content %}
    <div class="container">
        <header class="jumbotron my-4">
            <h1 class="display-3">{{ question.title }}</h1>
        </header>
        <div class="card-body">
            内容:<br>
            {{ question.body }}
        </div>
        <a href="{{ url_for('questions.find_all')}}">戻る</a>
        <a href="{{ url_for('questions.update', question_id=question.id) }}">修正</a>
    </div>
{% endblock %}

 

見た目はこのようになります

bootstrapの適用はこれで完了です

Bitbucketのリンク:https://bitbucket.org/Masahiro_Okubo/qa-site/src/phase2/
ブランチは「phase2」で指定してください。

 

参考記事

【Trainer’s Recipe】Pythonのフレームワークのflaskを触ってみた。