このページでは、Flaskを使い、FizzBuzz問題の結果をブラウザで表示します。
そのなかで、
- HTML内でforとif文を使う方法(Jinja2)
- CSSファイルを適用させる方法
- 動的ページの作成方法
を見ていきます。
Pythonの基本的なこと、HTMLとCSSの多少の知識があること、を前提としています。
Flaskが初めての人は↓から。
参考ページ
Flaskへようこそ ―― Flask Documentation
Jinja ―― Jinja Documentation
使用した環境
WSL2 Ubuntu - 20.04
Python 3.10.0
Flask 2.1.3
モジュールとHTMLの用意
下準備としてモジュールを作成します。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
モジュールと同階層の「templates」内につぎのHTMLファイルを追加します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>FizzBuzz</title>
</head>
<body>
<!-- これから書いていきます -->
</body>
</html>
bodyの内容はまだありません。
次の項目で埋めていきます。
HTML内でforとifを使う
FizzBuzz問題を知らない人のために簡単に説明すると、数字を1から順に表示させていきますが、そのうち3の倍数のときは「Fizz」、5の倍数だと「Buzz」、3と5両方の倍数のときは「FizzBuzz」を表示させるというものです。
これを100までHTMLで書こうとすると大変ですが、Flaskではテンプレート(HTML)の中でfor文とif文、そして変数を使うことができます。
厳密にはFlaskではなく「Jinja2」というテンプレートエンジンの機能で、Flaskがインストールされるときに自動で追加されています。
$ pip list Package Version ------------ ------- click 8.1.3 Flask 2.1.3 itsdangerous 2.1.2 Jinja2 3.1.2 <-- MarkupSafe 2.1.1 pip 22.2.1 setuptools 57.4.0 Werkzeug 2.1.2
今回使うものの構文は次のような形です。
forの構文
{% for ~%}
処理
{% endfor %}
ifの構文
{% if 条件式 %}
処理
{% elif 条件式 %}
処理
{% else %}
処理
{% endif %}
変数などの使用
{{ 変数名 }}
ではFizzBuzzを解きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>FizzBuzz</title>
</head>
<body>
{% for i in range(1, 101) %}
{% if i % 15 == 0 %}
<p>FizzBuzz</p>
{% elif i % 3 == 0 %}
<p>Fizz</p>
{% elif i % 5 == 0 %}
<p>Buzz</p>
{% else %}
<p>{{ i }}</p>
{% endif %}
{% endfor %}
</body>
</html>
波カッコや%を使っていて、forとifにはそれぞれend文が必要です。
Pythonにおける構文とそれほど変わらず、難しいことはないと思います。
※ちなみにPythonのときとは違い、インデントをしなくてもエラーにはなりません。
100行分を手打ちするより楽に短く書くことができました。
では開発サーバーにアクセスしてみます。
$ flask run * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) * Restarting with stat * Debugger is active!


FizzBuzzの結果が表示されました。
CSSファイルを適用させる
CSSを適用させるにあたってテンプレートに変更を加えました。
主な変更点はクラスの付与です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>FizzBuzz</title>
</head>
<body>
<div>
<h1>FizzBuzzを表示しよう</h1>
{% for i in range(1, 101) %}
{% if i % 15 == 0 %}
<p class="fizzbuzz">FizzBuzz</p>
{% elif i % 3 == 0 %}
<p class="fizz">Fizz</p>
{% elif i % 5 == 0 %}
<p class="buzz">Buzz</p>
{% else %}
<p>{{ i }}</p>
{% endif %}
{% if i % 10 == 0 %}
<br>
{% endif %}
{% endfor %}
</div>
</body>
</html>
そして適用させるCSSはこちらを用意しました。
div {
width: 900px;
text-align: center;
}
p {
display: inline-block;
width: 80px;
text-align: center;
}
.fizzbuzz {
font-weight: bold;
color: red;
}
.fizz {
color: orange;
}
.buzz {
color: blue
}
基本的に静的ファイル(CSSや画像など)を置く場所は、アプリケーションモジュールと同階層の「static」という名前のディレクトリ内です。
同名のディレクトリを作ってその中にCSSファイルを入れてください。
$ tree . ├── app.py ├── static │ └── style.css <-- └── templates └── index.html 2 directories, 3 files
CSSを読み込む設定をHTML内でします。
私のテンプレートでは既に追記済みですが、次の文をheadの中に記述します。
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
ここで使う「url_for」というのはFlaskの関数で、第一引数は'static'です。
2つ目の引数に「filename='用意したCSSファイルの名前'」を渡します。
それではサーバーを起動して、どう変化するか確認してみましょう。
$ flask run * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) * Restarting with stat * Debugger is active!

CSSが適用されました。
動的なページの作成
URLとして入力された文字列を使い、それをテンプレートに反映させる、ということをやります。
動的ページの作成です。
具体的にいうと、ルートにアクセスしたときはそのままFizzBuzzを表示し、たとえば「/three/five」にアクセスしたときには、Fizzの代わりに「three」、Buzzの代わりに「five」、FizzBuzzの代わりに「threefive」と表示されるようにします。
まずモジュールを変更します。
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
@app.route('/<fizz>/<buzz>')
def index(fizz='Fizz', buzz='Buzz'):
return render_template('index.html', fizz=fizz, buzz=buzz)
- 変数として使いたいURLの部分を<>で挟み、その中で名前を付けます。
- 関数の引数にその変数を渡します。上記ではデフォルト値が設定済みです。
- render_template関数の引数、ファイル名の次に「HTML内で使う変数名=変数」の形で渡します。
受け取る変数は型を指定することができます。
- string 文字列型(/を除く)
- int 正の整数
- float 正の浮動小数点
- path /を含めた文字列
- uuid UUID文字列(UUIDとは)
使用例
@app.route('/<string:fizz>')
次にテンプレートの変更。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>FizzBuzz</title>
</head>
<body>
<div>
{% set fizzbuzz = fizz + buzz %}
<h1>FizzBuzzを表示しよう</h1>
{% for i in range(1, 101) %}
{% if i % 15 == 0 %}
<p class="fizzbuzz">{{ fizzbuzz }}</p>
{% elif i % 3 == 0 %}
<p class="fizz">{{ fizz }}</p>
{% elif i % 5 == 0 %}
<p class="buzz">{{ buzz }}</p>
{% else %}
<p>{{ i }}</p>
{% endif %}
{% if i % 10 == 0 %}
<br>
{% endif %}
{% endfor %}
</div>
</body>
</html>
モジュールで定義した変数を使っています。
{{ fizz }} と {{ buzz }} がありますよね。
divタグのひとつ下の行は、Jinja2のテンプレート内で変数を定義や再定義する文で、setキーワードを使っています。
それでは出来ているか確認してみましょう。
$ flask run * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) * Restarting with stat * Debugger is active!
ルートページにアクセスしたときは前項と同じなので割愛します。
ブラウザで「http://127.0.0.1:5000/three/five」にアクセスすると、

意図通りに変わりました。
ちょっと不満なのはFizzとBuzzのように頭文字が大文字になっていないところです。
せっかくなので変えましょう。
divの開始タグの下につぎの文を追加します。
{% set fizz = fizz.capitalize() %}
{% set buzz = buzz|capitalize %}
fizzとbuzzの中身の頭文字を大文字にし、同名の変数に代入しています。
ひとつ目がPythonの文字列メソッドによる変換。
ふたつ目がJinjaのフィルター(メソッドのようなもの)による変換です。
Pythonの機能のすべてをJinjaで使えるようにしているわけではありません。
※例えば、よく使いそうな関数――enumerateは使えません。代わりにJinjaではfor文の内側で使える「loop.index」という特別な変数が用意されています。
フィルターはJinjaで実装している機能群で、他にもあるので以下を参照してください。
List of Builtin Filters [Jinja Documentation]
変更点を保存し、サーバーを起動します。

「http://127.0.0.1:5000/foo/bar」にアクセスしました。
今度は頭文字が大文字です。
細かいことを言えば、見出しの内容も変えた方が良いでしょうし、titleタグの上で変数を定義すれば、タイトルも変えられますが、ここで止めておきます。
注意事項
URLの一部を変数として使い、それをJinjaを通さずにHTMLとして返すような場合には、インジェクション攻撃を防ぐためにエスケープ処理が必要です。
Jinjaを使ってテンプレート内で変数を使うときには必要ありません。
Jinjaがエスケープ処理を自動でやってくれます。
Flaskインストール時に追加されたモジュール「markupsafe」の「escape関数」を使います。
$ pip list Package Version ------------ ------- click 8.1.3 Flask 2.1.3 itsdangerous 2.1.2 Jinja2 3.1.2 MarkupSafe 2.1.1 <-- pip 22.2.1 setuptools 57.4.0 Werkzeug 2.1.2
from flask import Flask
from markupsafe import escape
app = Flask(__name__)
@app.route('/<name>')
def print_name(name):
return f'Your Name: {escape(name)}'