Python
Heroku
bootstrap
bottle

PythonのBottleとBootstrapでInstagramみたいなのを作る(第一弾)

1. 目的

Webアプリケーションを作りたいという気持ちを実現するために、練習としてPythonのBottleとBootstrapを使った簡単なウェブアプリケーションを作る。
題材はインスタグラムライクなアプリケーションで、画像と文章を投稿できるサービスを目指す。

2. とりあえず

今回は第一弾ということで、本当に基礎的な部分だけを作っていくことにする。
用意した画像とテキストを表示するところまでやってみる。

3. 目標完成図

・headerにロゴをつける
・bodyに白枠のフィールドがあり、そこに画像とテキストを載せる
・このフィールドをいくつか並べる

4. やってみる

  • project作成

"Chenstagram"という名前でプロジェクトを作成しました。
中にchenstagram.pyとuserpage.htmlのファイルを作成しました。

  • html作成

Bootstrapのtutorialページにアクセスし、get startedのコードをコピペします。
script部分にBootstrapCDNがあるので、これでBootstrapが使えるようになります。

userpage.html
<!doctype html>
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <title>Chenstagram</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </body>
</html>

言語設定をjaに変えて、タイトルもChenstagramに変えておきます。

  • bottleでトップページを表示

続いて、bottle.pyを編集してuserpage.htmlを表示させます。
bottleをimportしてappというインスタンスを作ります。
/<username>(<username>は任意)にアクセスした時に、username.htmlを表示するように書いています。
一応、あとあとのために<username>を渡す処理も書いています。

最後にdebugとreloaderをTrueにしてデバッグしやすいようにしてからrunさせます。
実際にweb上にあげるときはdebugとreloaderはFalseにします。

chenstagram.py
from bottle import Bottle, template
import os

app = Bottle()

@app.get("/<username>")
def userpage(username) :
    return template("userpage" , username = username)

@app.route("/")
def index() :
    return "<h1> hello my website!</h1>"

app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)) , reloader=True , debug = True)
  • htmlを編集して構成を作る

さて、続いてhtmlを編集して構成をinstagramっぽくします。
bootstrap公式のExamplesから使えそうなテンプレートを探します。今回はalbumを参考にしたいと思います。

まずはヘッダーから。かっこいいので丸パクリします。
ただしトグルは除いて、ヘッダーの部分だけもらいます。

userpage.html
    <div class="navbar navbar-dark bg-dark box-shadow">
        <div class="container d-flex justify-content-between">
          <a href="#" class="navbar-brand d-flex align-items-center">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle></svg>
            <strong>Chenstagram</strong>
          </a>
        </div>
    </div>

内容についてドキュメントで調べて理解していきます。
navbarというのはナビバーのことです。navbar-darkで暗い色の背景になり、bg-darkでその色を決めます。box-shadowは影をつけています。ちょっと見た目でどこに影がついているのかは分かりませんでした・・・
続いてその中に、containerを作ってひとまとめにしています。d-flexとはdisplay flexible boxのことで、要するにこの中でひとまとまりにしますということだと思います。justify-contentはこのまとまりの中のitemをどう配置するかを決めており、betweenはstartとendの間に均等に配置しているようです。今回はロゴしか挿入しないため、betweenでもstartでも見た目は変わりませんが、headerに新たに検索欄などを追加することを考えるとbetweenの方が見た目が良さそうに思えます。
その中がリンクで#に飛ぶロゴになっています。navbar-brandはサービス名や会社名のためのクラスです。align-items-centerでy軸方向に中央揃えで配置しています。
svgでカメラのロゴを作り、そのあとにサービス名のChenstagramをstrongタグで強調しています。今回は消してしまいましたが、さらに続けてトグルや検索窓を追加することができます。

次にbodyを記述していきます。
Cardを使って画像とテキストを収めるフィールドを作ります。

userpage.html
      <div class="pt-5 bg-light">
        <div class="container">

          <div class="row">

            <div class="col-lg-6 mb-5">
              <div class="card lg-6 box-shadow"  style="max-width: 30rem;">
                <img class="card-img-top" data-src="" alt="Card image cap">
                <div class="card-body">
                  <h5 class="card-title">@{{username}}</h5>
                  <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <div class="d-flex justify-content-between align-items-center">
                    <div class="btn-group">
                      <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                      <button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
                    </div>
                    <small class="text-muted">9 mins</small>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

最初のdivでheaderとの距離をpt-5(padding top 5)とり、背景色を明るい灰色にします。
次のdivでcontainerを作り、その中にgrid systemを作ります。rowは一つでよいみたいですね。
その中にcolをかくわけですが、画面が大きい時だけ二列にするためにcol-lg-6を指定します(grid systemでは合計が12を越えると次の行へ行くので、6にしておくと2列になる。)
その中にcardをかき、max-widthを30remに指定しておきます。
上側に画像を置くために、imgタグのcard-img-topで上側に配置されます。data-srcはとりあえず空です。
続いてテキスト部分にあたるcard-bodyの中にcard-titleとしてユーザー名を、card-textとして説明文を加えます。
その下にボタンや時間はコピペでとりあえずくっつけておきます。

  • 画像とテキストを用意

あとは画像とテキストを用意してそれを読み込むようにしましょう。
imgフォルダをプロジェクトの中に作り、1.jpg 2.jpg 3.jpgという画像ファイルを入れておきます。
chenstagram.pyに画像の置き場所を作っておきます。

chenstagram.py
@app.route("/img/<filepath>")
def imgs(filepath):
    return static_file(filepath , root="./img")

ついでに、テキストもリストで用意しておきます。pythonのインデックスが0から始まる都合上、0番目は空にしておきました。

chenstagram.py
texts = [
    ""
    ,
    """
    素敵なシャネルのバッグです。シャネルのバッグは数十万円から百万円以上の値段で取引されており、その人気が伺える商品になっております。\nなんて
    素敵なんだろう。いやー素晴らしい。\nこんな素晴らしいものがこの世にあるだろうか?いやない。あるはずがない。なぜならシャネルだから。シャネルは素晴らしい。なんて素晴らしいのだろう
    そう思いませんか?"""
    ,
    """
    すっご〜〜い!インスタグラムのパクリが作れるフレンドなんだね!
    """
    ,
    """
    そら きれい
    """
]

@app.get("/<username>")
def userpage(username) :
    return template("userpage" , username = username , texts = texts)

  • htmlに反映させる 今回はファイル名を数字の連番にしたことでfor文で簡単に呼び出せます。
userpage.html
<div class="pt-5 bg-light">
        <div class="container">

          <div class="row">
              %for i in range(1,4) :
            <div class="col-lg-6 mb-5">
              <div class="card lg-6 box-shadow" >
                <img class="card-img-top" src="../img/{{i}}.jpg" alt="Card image cap">
                <div class="card-body">
                  <h5 class="card-title">@{{username}}</h5>
                  <p class="card-text">{{texts[i]}}</p>
                  <div class="d-flex justify-content-between align-items-center">
                    <div class="btn-group">
                      <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                      <button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
                    </div>
                    <small class="text-muted">9 mins</small>
                  </div>
                </div>
              </div>
            </div>
            %end

          </div>
        </div>
      </div>

  • 完成!

とりあえずざっくりとこんな感じで出来ました!

完成図.jpg

  • 課題

課題は山積していますが、とりあえず画像とテキストがいまは直接呼び出す形になっていますが、データベースから読み込む形にする必要があると思います。
画像フォルダもユーザーごとに分けて保存する必要があります。
あとはおいおい・・・