話題のHTMXを試して見ました。
WebアプリケーションフレームワークはFlaskです。
HTMXを使った場合と使ってない場合を比較したかったので、最初にHTMXを使ってないアプリを作りました。以下はChatGPTさんに「ボタンを押下すると表示された数値がカウントされる、簡単なアプリの例をお願いします。」で生成したコードそのままです。
app.py
from flask import Flask, render_template_string, request, redirect, url_for
app = Flask(__name__)
# カウンターの値を保持する変数
counter = 0
# HTMLテンプレート
html = """
<!doctype html>
<html>
<head><title>Counting App</title></head>
<body>
<h1>Counter: {{ counter }}</h1>
<form action="/count" method="post">
<input type="submit" value="Count">
</form>
</body>
</html>
"""
@app.route('/')
def index():
global counter
return render_template_string(html, counter=counter)
@app.route('/count', methods=['POST'])
def count():
global counter
counter += 1 # ボタンが押されるたびにカウンターを増やす
return redirect(url_for('index')) # メインページにリダイレクト
if __name__ == '__main__':
app.run(debug=True)
これだけだと面白くないかなと思い、+2されるボタンを追加して見ました。これはChatGPTさんでなく自分で変更。
app2.py
from flask import Flask, render_template_string, request, redirect, url_for
app = Flask(__name__)
# カウンターの値を保持する変数
counter = 0
counter2 = 0
# HTMLテンプレート
html = """
<!doctype html>
<html>
<head><title>Counting App</title></head>
<body>
<h1>Counter: {{ counter }}</h1>
<form action="/count" method="post">
<input type="submit" value="Count">
</form>
</body>
<body>
<h1>Counter2: {{ counter2 }}</h1>
<form action="/count2" method="post">
<input type="submit" value="Count2">
</form>
</body>
</html>
"""
@app.route('/')
def index():
global counter
global counter2
return render_template_string(html, counter=counter, counter2=counter2)
@app.route('/count', methods=['POST'])
def count():
global counter
counter += 1 # ボタンが押されるたびにカウンターを増やす
return redirect(url_for('index')) # メインページにリダイレクト
@app.route('/count2', methods=['POST'])
def count2():
global counter2
counter2 += 2 # ボタンが押されるたびにカウンターを増やす
return redirect(url_for('index')) # メインページにリダイレクト
if __name__ == '__main__':
app.run(debug=True)
上のボタンは押下するたびに1加算されます。下のボタンは押下するたびに2加算されます。
次はHTMXを使ったパターン。ここでまたまたChatGPTさんの登場。「app2.pyですが、HTMX使うとどうなりますか」と聞いて生成したのが下記のコード。
app3.py
from flask import Flask, render_template_string, request, jsonify
app = Flask(__name__)
# カウンターの値を保持する変数
counter = 0
counter2 = 0
# HTMLテンプレート
html = """
<!doctype html>
<html>
<head>
<title>Counting App</title>
<script src="https://unpkg.com/htmx.org"></script> <!-- HTMXライブラリを追加 -->
</head>
<body>
<div id="counter1">
<h1>Counter: {{ counter }}</h1>
<form hx-post="/count" hx-target="#counter1" hx-swap="outerHTML">
<input type="submit" value="Count">
</form>
</div>
<div id="counter2">
<h1>Counter2: {{ counter2 }}</h1>
<form hx-post="/count2" hx-target="#counter2" hx-swap="outerHTML">
<input type="submit" value="Count2">
</form>
</div>
</body>
</html>
"""
@app.route('/')
def index():
global counter, counter2
return render_template_string(html, counter=counter, counter2=counter2)
@app.route('/count', methods=['POST'])
def count():
global counter
counter += 1
return render_template_string('<div id="counter1"><h1>Counter: {{ counter }}</h1><form hx-post="/count" hx-target="#counter1" hx-swap="outerHTML"><input type="submit" value="Count"></form></div>', counter=counter)
@app.route('/count2', methods=['POST'])
def count2():
global counter2
counter2 += 2
return render_template_string('<div id="counter2"><h1>Counter2: {{ counter2 }}</h1><form hx-post="/count2" hx-target="#counter2" hx-swap="outerHTML"><input type="submit" value="Count2"></form></div>', counter2=counter2)
if __name__ == '__main__':
app.run(debug=True)
"""
@app.route('/')
def index():
global counter, counter2
return render_template_string(html, counter=counter, counter2=counter2)
@app.route('/count', methods=['POST'])
def count():
global counter
counter += 1
return render_template_string('<div id="counter1"><h1>Counter: {{ counter }}</h1><form hx-post="/count" hx-target="#counter1" hx-swap="outerHTML"><input type="submit" value="Count"></form></div>', counter=counter)
@app.route('/count2', methods=['POST'])
def count2():
global counter2
counter2 += 2
return render_template_string('<div id="counter2"><h1>Counter2: {{ counter2 }}</h1><form hx-post="/count2" hx-target="#counter2" hx-swap="outerHTML"><input type="submit" value="Count2"></form></div>', counter2=counter2)
if __name__ == '__main__':
app.run(debug=True)
実行すると以下のような画面になります。
こちらも上のボタンは押下するたびに1加算されます。下のボタンは押下するたびに2加算されます。見た目はapp2.pyと変わりませんが、ボタンを押下した時にページ全体がリロードされないので画面がチラチラしません。HTMXにした効果がちゃんと出ているようです。
最後に
実はWebアプリはあまりやったことがなくて、PythonとFlaskも初めて使いました。ChatGPTさんにこんなことやりたいのだけど、どうすりゃいいの? って聞きながら 。