3日目のゴール
3日目のテーマは
「Flaskのルーティングを“現場で使えるレベル”まで一段深くする」 ことです。
1日目:固定URL(/, /hello)
2日目:動的URL(/user/<name>, /item/<int:id>)
3日目ではここからさらに進んで、
URLの「?以降」(クエリパラメータ)を扱う
同じURLでも、処理を分けるイメージを持つurl_for で「URLを文字列でベタ書きしない」感覚をつかむ
このあたりを、ちゃんと自分の言葉で説明できるところまで持っていきます。
復習:Flaskのルーティングの基本形
URL → 関数 → 返り値、という流れ
まずは、土台をもう一度確認します。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "こんにちは Flask!"
if __name__ == "__main__":
app.run(debug=True)
ここで起きていることは、とてもシンプルです。
@app.route("/") で「/ に来たら index 関数を呼ぶ」と登録している。
ブラウザが http://127.0.0.1:5000/ にアクセスすると、Flaskが index を呼ぶ。
index が return した文字列が、そのままブラウザに返る。
2日目では、ここに「URLの一部を変数にする」という考え方を足しました。
@app.route("/user/<name>")
def show_user(name):
return f"こんにちは、{name} さん!"
3日目では、「URLの?以降」も扱えるようにしていきます。
クエリパラメータとは何かをイメージでつかむ
?keyword=python&page=2 の「?以降」の世界
ブラウザで、こんなURLを見たことがあると思います。
https://example.com/search?keyword=python&page=2
? より前が「パス」? より後ろが「クエリパラメータ」
クエリパラメータは、
「同じページだけど、条件を変えたいとき」に使われます。
検索キーワード
ページ番号
並び順
などを指定するのに、よく使われます。
Flaskでは、この「?以降」の値を request.args から取り出します。
request.args でクエリパラメータを読む
検索風のルートを作ってみる
まずは、シンプルな「検索っぽいページ」を作ってみましょう。
from flask import Flask, request
app = Flask(__name__)
@app.route("/search")
def search():
keyword = request.args.get("keyword")
page = request.args.get("page", default="1")
if not keyword:
return "検索キーワードを ?keyword=xxx の形で指定してください。"
return f"検索キーワード: {keyword}, ページ: {page}"
if __name__ == "__main__":
app.run(debug=True)
Pythonここでの重要ポイントを深掘りします。
from flask import request で「リクエスト情報」にアクセスできるようにしている。request は「今来ているリクエスト」の情報を持っている特別なオブジェクトです。
request.args は「クエリパラメータ(?以降)」を表す。request.args は辞書のように扱えます。request.args.get("keyword") で keyword の値を取り出せます。
get("page", default="1") のように、デフォルト値を指定できる。page が指定されていないときは "1" を使う、という意味です。
ブラウザで次を試してみてください。
http://127.0.0.1:5000/searchhttp://127.0.0.1:5000/search?keyword=pythonhttp://127.0.0.1:5000/search?keyword=flask&page=3
URLの「?以降」を変えるだけで、表示内容が変わる感覚をつかんでほしいです。
パスパラメータとクエリパラメータの違いを整理する
/user/1 と /user?id=1 の違い
2日目でやった「動的ルーティング」は、URLのパスの一部を変数にしました。
/user/1/user/2
これは、ルート定義としてはこうでした。
@app.route("/user/<int:user_id>")
def show_user(user_id):
...
Python一方、クエリパラメータを使うと、こういう形になります。
/user?id=1/user?id=2
ルート定義は固定で、/user のままです。
@app.route("/user")
def show_user():
user_id = request.args.get("id")
...
Pythonどちらも「IDを指定する」という意味では似ていますが、
使い分けのイメージはこうです。
パスパラメータ(/user/1)
「そのリソースそのもの」を表すことが多い。
ユーザー1のページ、記事10のページ、など。
クエリパラメータ(/search?keyword=python&page=2)
「同じページだけど条件を変える」イメージ。
検索条件、ページ番号、並び順など。
今の段階では、
「どちらも使えるけど、意味合いが少し違う」
くらいの理解で十分です。
例題:ユーザー一覧+検索風ページを作る
/users と /users?name=xxx を同じ関数で処理する
2日目の「疑似ユーザーデータ」を使って、
クエリパラメータで絞り込みをしてみましょう。
from flask import Flask, request
app = Flask(__name__)
USERS = [
{"id": 1, "name": "Taro"},
{"id": 2, "name": "Hanako"},
{"id": 3, "name": "Ken"},
{"id": 4, "name": "Taroko"},
]
@app.route("/users")
def list_users():
keyword = request.args.get("name")
if not keyword:
lines = ["全ユーザー一覧:"]
for user in USERS:
lines.append(f"{user['id']}: {user['name']}")
return "<br>".join(lines)
keyword_lower = keyword.lower()
filtered = []
for user in USERS:
if keyword_lower in user["name"].lower():
filtered.append(user)
if not filtered:
return f"'{keyword}' を含むユーザーは見つかりませんでした。"
lines = [f"'{keyword}' を含むユーザー一覧:"]
for user in filtered:
lines.append(f"{user['id']}: {user['name']}")
return "<br>".join(lines)
if __name__ == "__main__":
app.run(debug=True)
Pythonここでの本質を整理します。
同じ /users というURLで、「全件表示」と「絞り込み表示」を両方やっている。name パラメータがなければ全件表示。name パラメータがあれば、その値で絞り込み。
request.args.get("name") で、クエリパラメータを受け取っている。None や空文字のときは「指定なし」と判断している。
このパターンは、実際のWebアプリでもよく使います。
「同じページだけど、条件によって中身が変わる」という感覚を、ここでつかんでおきましょう。
url_for で「URLをベタ書きしない」感覚を持つ
URLを文字列で書くと、あとで必ずつらくなる
1日目・2日目では、リンクをこう書いていました。
return '<a href="/profile">プロフィール</a>'
Pythonこれは動きますが、
URLを文字列でベタ書きしているので、
あとでURLを変えたくなったときに、全部探して直す必要が出てきます。
Flaskには、
「関数名からURLを逆算してくれる」 url_for という便利な関数があります。
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/")
def index():
profile_url = url_for("profile")
return f'<a href="{profile_url}">プロフィールへ</a>'
@app.route("/profile")
def profile():
return "プロフィールページです。"
if __name__ == "__main__":
app.run(debug=True)
Pythonここでの重要ポイントは、
url_for("profile") は、「profile 関数に対応するURL」を返す。
今は /profile ですが、もし将来ルートを /user/profile に変えても、url_for("profile") を使っていれば、コードを直さなくて済みます。
url_for の引数は「関数名」である。@app.route で登録した関数の名前を渡します。
文字列のURLではなく、「どの関数か」で指定するのがポイントです。
3日目の段階では、
「URLをベタ書きするより、url_for を使った方が後々楽になる」
という感覚だけ持っておけばOKです。
3日目のミニアプリ:簡易ユーザー検索サイト
仕様を言葉で整理する
/ トップページ:使い方の説明/users:全ユーザー一覧/users?name=xxx:名前に xxx を含むユーザーだけ表示url_for を使ってリンクを作る
これを一つのファイルにまとめてみます。
from flask import Flask, request, url_for
app = Flask(__name__)
USERS = [
{"id": 1, "name": "Taro"},
{"id": 2, "name": "Hanako"},
{"id": 3, "name": "Ken"},
{"id": 4, "name": "Taroko"},
]
@app.route("/")
def index():
users_url = url_for("list_users")
return f"""
<h1>ユーザー検索ミニアプリ</h1>
<p><a href="{users_url}">ユーザー一覧を見る</a></p>
<p>名前で絞り込みたいときは、URLの末尾に ?name=ta のように付けてください。</p>
"""
@app.route("/users")
def list_users():
keyword = request.args.get("name")
if not keyword:
lines = ["<h2>全ユーザー一覧</h2>"]
for user in USERS:
lines.append(f"{user['id']}: {user['name']}")
return "<br>".join(lines)
keyword_lower = keyword.lower()
filtered = []
for user in USERS:
if keyword_lower in user["name"].lower():
filtered.append(user)
if not filtered:
return f"<h2>検索結果</h2>'{keyword}' を含むユーザーは見つかりませんでした。"
lines = [f"<h2>検索結果: '{keyword}' を含むユーザー</h2>"]
for user in filtered:
lines.append(f"{user['id']}: {user['name']}")
return "<br>".join(lines)
if __name__ == "__main__":
app.run(debug=True)
Pythonこのミニアプリで、3日目のポイントが全部入っています。
クエリパラメータ(?name=xxx)を request.args.get で受け取る。
同じ /users というURLで、「全件表示」と「絞り込み表示」を切り替える。url_for("list_users") で、URLを関数名から生成する。
3日目で絶対に押さえてほしい本質
「URLの“パス”と“?以降”を両方コントロールできる」
今日いちばん大事なのは、
Flaskのルーティングをこう説明できることです。
URLの「パス部分」(/users/1 のようなところ)は、@app.route("/users/<int:id>") で変数として受け取れる。
URLの「?以降」(?name=xxx&page=2 のようなところ)は、request.args.get("name") や request.args.get("page") で受け取れる。
同じURLでも、クエリパラメータの有無や値によって、
返す内容を変えることができる。
そして、URLを文字列でベタ書きする代わりに、url_for("関数名") で「関数からURLを逆算する」ことができる。
ここまで来たあなたは、
もう「Flaskでページを出せる人」ではなく、
「URL設計とパラメータ設計を意識してWebアプリを組み立てられる人」 です。
4日目以降は、
ここに「テンプレート(HTMLファイル)」や「フォーム送信(POST)」を重ねていきます。
でも、どれだけ機能が増えても、
今日やった「パス」と「クエリ」の扱い方が、ずっと土台になり続けます。

