Semantic UIをFlaskで導入する
はじめに
PythonのWebアプリケーションフレームワークFlaskを使って、「消耗品買い物リスト」を作ってみるシリーズ第15回目です。
見た目を最低限整えるために、デザインフレームワークSemantic UIを導入します。
前回まで
- 0回 : Flaskで簡単なWebサービスを作ってみる
- 1回 : スプレッドシート+Google Apps Scriptでプロトタイプを作る
- 2回 : FigmaでUI制作をしてみたが早々に次のフェーズに進んだ件
- 3回 : DockerでFlaskが動き、簡単なテストが通る状態を作る
- 4回 : [スクショ付き]AWSにVPCを構築し、EC2を立てる
- 5回 : EC2でDockerコンテナを起動し、Flaskを動かす
- 6回 : CircleCIでFlaskアプリの自動テストができるようにする
- 7回 : CircleCIでIP制限のあるEC2インスタンスに自動デプロイできるようにする
- 8回 : ユーザーストーリーマッピングで初期開発機能を洗い出す
- 9回 : [Flask]uuidを生成して専用URLを作成する
- 10回 : flask-sqlalchemy/flask-migrate/mysql-connector-pythonでMySQL連携を実装する
- 11回 : [Flask-SQLAlchemy]レコードの登録機能の実装
- 12回 : [Flask-SQLAlchemy]レコード編集と論理削除による削除機能の実装
- 13回 : [Flask-SQLAlchemy]レコード更新ログを実装する/autoincrementの値をレコード挿入時に取得する方法
- 14回 : [Flask]Flashメッセージを実装する
インストール
公式サイトのGetting Startedを愚直にやります。
どこにインストールするかですが、Flaskで静的コンテンツを配置するtbl/static直下に各種ディレクトリを置くことにします。
# node.jsのインストール
brew install node
# Gulpのインストール
npm install -g gulp
# ディレクトリ移動
$ cd tbl/static
# Semantic UIインストール
npm install semantic-ui --save
cd semantic/
gulp build
プロジェクトフォルダは現在地で良いか?どこにSemantic UIを配置するか?を聞かれますが、デフォルトで進めました。
インストールが完了すると、ビルドしたディレクトリ(ここでいうとstatic直下)に
- node_modules
- package-lock.json
- semantic
- semantic.json
のファイル/ディレクトリが作られます。
semanticディレクトリを見てみます。
$ ls -1 tbl/static/semantic/
dist
gulpfile.js
src
tasks
上記のdistディレクトリの中に実際に使うCSSやJSが格納されています。
セットアップ
インストールできたので、使えるか試します。
headでCSS/JSを読み込む
まずはSemantic UIのCSS/JSを読み込みむために、基本のテンプレートファイルを編集します。
- tbl/templates/application.html
<head>
...
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='semantic/dist/semantic.min.css') }}">
<script src="{{ url_for('static', filename='semantic/dist/semantic.min.js') }}"></script>
...
</head>
※jQueryは前段ですでに読み込んでいるので割愛してます。
ヘッダーをつける
共通のヘッダーをつけます。
- tbl/templates/application.html
...
<body>
<div class="ui inverted large borderless menu column">
<a class="header item" href="/">Listem</a>
</div>
<div class="ui container">
{% block body %}
{% endblock %}
</div>
</body>
...
<div class="ui container">
でコンテンツ部分をいい感じに囲んでおきます。
トップページを整える
最初のページを整えます。
ついでにキャッチコピーやサービス名も仮で入れておきます。
サービス名は仮で「Listem」にしました。
- index.html
{% extends "application.html" %}
{% block body %}
<div class="ui container">
<h1>Listem</h1>
<p>買い忘れを無くしましょう。</p>
<button class="ui button teal" onclick="location.href='page/create'">
あなたの日用品買い物リストを作る
</button>
</div>
{% endblock %}
<a href="...">
でリンクを貼っていたものは、ボタンに差し替えています。
<button class="ui button teal" onclick="location.href='page/create'">
こんな感じになります。
アイテム一覧ページを整える
メインのアイテム一覧ページを差し替えていきます。
Flashメッセージ
- user_page.html
...
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class='flash ui success message'>
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
...
<div class='flash ui success message'>
で囲むようにしました。
アイテムリスト
- user_page.html
...
<div class="ui middle aligned divided list">
{% if page.items.count() > 0 %}
{% for p in page.items %}
<div class="item">
<div class="right floated content">
<div class="ui icon button">
<a href="{{ url_for('edit_item', item_id=p.id, uuid=page.uuid) }}"><i class="edit icon"></i></a>
</div>
<div class="ui icon button">
<a href="{{ url_for('delete_item', item_id=p.id, uuid=page.uuid) }}"><i class="trash icon"></i></a>
</div>
</div>
<div class="content">
<div class="header">{{ p.name}} </div>
Last update: {{ p.updated_at }},
Cycle days: {{ p.purchase_cycle_days }},
Stock rate: {{ p.stock_rate }},
Status: {{ p.status }}
</div>
</div>
{% endfor %}
{% else %}
<p>No item set yet.</p>
{% endif %}
<div class="item">
<form name="input" class="ui form" id="new_item" action="item/create" accept-charset="UTF-8" method="post">
{% with submit_word = 'Add Item' %}
{% include "item_form.html" %}
{% endwith %}
</form>
</div>
</div>
...
<div class="ui middle aligned divided list">
で、子要素を線で区分けされたリストにすることができます。- 子要素は
<div class="item">...</div>
で作ります。 <div class="right floated content">...</div>
には編集/削除ボタンを入れています。<div class="ui icon button">...<i class="XXX icon"></i></div>
と書くと、Semantic UIが用意しているアイコン画像を利用できます。
<div class="content">...</div>
にはアイテム情報を記載しています。{% endif %}
以降は、新規アイテム追加用のフォームになっています。<form ... class="ui form">
とすることでSemantic UIの用意したFormがつかえます。
アイテムフォーム部品の変更
user_page.htmlで呼び出しているフォームの部品も差し替えます。
- item_form.html
<div class="ui form">
<div class="fields">
<div class="inline field">
<label>品目名: </label>
<input type="text" class="validate[required]" name="name" id="name"
{% if submit_word =='Edit item' %} value="{{ item.name }}"{% endif %}/>
</div>
<div class="inline field">
<label>購入サイクル(日数): </label>
<input type="number" class="validate[custom[number]]" name="purchase_cycle_days" id="purchase_cycle_days" min="1" max="90"
{% if submit_word =='Edit item' %} value="{{ item.purchase_cycle_days }}"{% endif %}/>
</div>
<div class="inline field">
<label>残量ステータス(0.0〜1.0): </label>
<input type="number" class="validate[custom[number]]" name="stock_rate" id="stock_rate" min="0.0" max="1.0" step="0.1"
{% if submit_word =='Edit item' %} value="{{ item.stock_rate }}"{% endif %}/>
</div>
<div class="right floated">
{% if submit_word == 'Edit item' %}
<input type="hidden" name="item" value="{{ item.id }}" />
<input type="hidden" name="user_page_id" value="{{ item.user_page_id }}" />
{% else %}
<input type="hidden" name="user_page_id" value="{{ page.id }}" />
{% endif %}
<button class="ui button" type="submit">{{ submit_word }}</button>
</div>
</div>
</div>
<div class="ui form">
で囲んでいること<div class="inline field">
にして一行にラベルと入力フィールドが収まるようにしていること
あたりが主な変更点になります。
アイテム編集ページも整える
あまり変えるところは無いですが、アイテム編集ページもuser_page.htmlの書き方に合わせて変更します。
- item_edit.html
{% extends "application.html" %}
{% block body %}
<div>
<h1>{{ title }} : {{ item.name }}</h1>
<div>
<form name="input" class="ui" id="new_item" action="/item/edit/{{item.id}}/{{uuid}}" accept-charset="UTF-8" method="post">
{% with submit_word = 'Edit item' %}
{% include 'item_form.html' %}
{% endwith %}
</form>
</div>
</div>
{% endblock %}
見た目確認
終わりに
Semantic UIを入れることで、少しだけマシな見た目になりました。
見た目をいじるのはめちゃくちゃ楽しいのですが、今いじると泥沼にハマるので、このあたりでとどめておきます。
次回以降はまた機能に戻り、必要な機能を作り込んでいきます。
ディスカッション
コメント一覧
まだ、コメントがありません