2歩戻ったら2.5歩進みたい

関東で働くweb developerのブログ

「Pythonでwebサービスを作る」学習メモ 1巻②

どうもこんにちは。きゃにすたーです。 前回に引き続いて「Pythonwebサービスを作る」の1巻学習メモです。

前回の記事はこちら。 canisterism.hatenablog.com

ソースはこちらに。

github.com

GETのURLパラメーターを受け取る

テンプレートからGETで指定されたURLのパラメーターやPOSTで送られてきたリクエストボディを受け取るにはreqestを使います。

from flask import Flask,render_template,request

app = Flask(__name__)

@app.route(/search/)
def search():
    q = request.args.get("q","")
    return q

こう書くと、http://localhost:5000/search?q=hogehogeの部分がqとして返り値になります。こんな感じですね。

f:id:canisterism:20180428231227p:plain

request.args.getの第2引数ですが、これは何も入らなかった時のデフォルト値みたいですね。

公式を当たると、以下のようにあります。

ImmutableMultiDict.get(key, default=None, type=None)

Return the default value if the requested data doesn’t exist.

werkzeug.ImmutableMultiDict.get — Flask API

ここを読むと、requestはwerkzeugというhttpsのインターフェースとして作られたライブラリの複数の辞書オブジェクトを束ねたImmutableMultiDictというオブジェクトが正体らしいです。

さらっと知ってる風に言いましたが、40分ぐらいstackoverflowとかgithubのソースとか当たって「全然出てこんやんけ!!!!」って言いながら調べてました。OSSのソースとか公式とか参照したことなかったのでいい経験になりました。

POSTで送られてきたリクエストボディの中身を受け取る

今度はPOSTで送られてきたformの中身を受け取ります。

@app.route("/login",methods=["POST"])
def login():
    if request.form["username"] and request.form["email"]:
        return render_template("check.html", email=request.form["email"]
                                , username=request.form["username"])

これは、/loginにPOSTメソッドでアクセスした時の処理ですので、別のhtmlから

<form method="POST" action="/login">
    <input placeholder="名前" name="username" type="text"/>
    <input placeholder="メール" name="email" type="text"/>
    <input value="送信" type="submit"/>            
</form>

こんな感じでformタグをつけてアクセスする必要があります。

これは感想ですが、情報をページ間で送ったりするのってもっと難しいコードを書くのかと思ってましたが、意外と簡単なんですね。というのももともと複雑な処理をライブラリで簡単にしてくれている先人たちのお陰なんでしょうが。感謝感謝。

バイナリデータの取得

バイナリデータって言ってますが、要するに文字データ以外の画像とかファイル類です。

上のhtmlと同じようにformタグでPOSTで送るわけですが、一部違う箇所があります。

<form method="POST" action="/upload" enctype="multipart/form-data">
    <input placeholder="名前" name="name" type="text"/>
    <input name="image" type="file"/>
    <input value="送信" type="submit"/>            
</form>

まず、formenctype="multipart/form-data"という属性と属性値が追加されました。 これは説明するとめっちゃ長くなるっぽいので知りたい人は以下のリンクをご覧ください。

mugenup-tech.hatenadiary.com

とりあえず現時点ではこれを書かないとファイルの中身が送信されずファイル名しか送信されない、ぐらいの理解です。いわゆるおまじないですね。

そして、inputタグのtypefileになっています。これはそのままの意味ですね。

@app.route("/upload", methods=["POST"])
def upload_file():
    if request.form["name"] and request.files["image"]:
        f = request.files["image"]
        filepath = "static/" + secure_filename(f.filename)
        f.save(filepath)

        return render_template("result.html", name=request.form["name"]
                                ,image_url=filepath)

色々書いててややこしいですが、基本的にはGETの時と同じで、requestから値を取り出して、render_templateで返します。これは画像ファイルを想定しており、f.save(filepath)で一旦保存して、result.htmlでパスを指定して直接呼び出しています。

また、受け取った画像ファイルをレンダリングしたhtmlですぐに表示して保存もしない場合は、一旦メモリで画像ファイルを持って表示するやり方もあるようです。こちらは今後の課題ということで。

誤植?

余談ですが、誤植っぽい箇所を見つけました。

result.htmlの、表示する画像のパスが

<image src="/image/hoge.png">

となっていますが、保存した先は/static/なので、本文の通りに実行するとFile does not existになりました。恐らく

<image src="/static/hoge.png">

こちらが正しいと思われます。

htmlにコードを埋め込む

html側に処理を埋め込むことができます。実際の開発でも頻繁に使われるようですね。

条件分岐

app.py側で乱数を作ってhtmlに渡し、

return render_template("index.html",random=random.random())

{% %}で括れば処理を埋め込むことができます。

{% if random > 0.5 %}
    <h1>hoge</h1>
{% else %}
    <h1>fuga</h1>
{% endif %}

この場合はリロードするたびにhogeかfugaが表示されます。

リストの展開

引数にl=["a","b","c"]を渡す。

<ul>
    {% for item in l %}
        <li>{{item}}</li>
    {% endfor %}
</ul>

これで、

・a

・b

・c

レンダリングされます。

辞書型にアクセス

基本的な書き方は上と同じです。

{% for item in l %}

でオブジェクトを回して、値の取り出しは、

{{item.name}}

という感じで指定していけばOK。

テンプレートの継承

基本的にhtmlテンプレートはhead部分なんかは同じことを書いてるので、共通部分は切り取ってまとめることができます。DRYというやつですね。わかります。

全部コードを載せるのは長くなるのでやめますが、基本的には親テンプレート側で共通化したい部分を(ここではtheme.htmlとします)

{% block ID名 %}
    <!-- ~共通部分~ -->
{% endblock %}

という感じで書き、子テンプレート側でまず1行目に親テンプレートを指定します。

{% extends "theme.html" %} 

さらにその下には

{% block ID名 %}
    このテンプレートでの処理や記述
{% endblock %}

という感じで書いていきます。

ちょっと正直自分でも継承はピンときていない部分があるので、もうちょっと色々書いていきたいと思います。

まとめ

これで1巻は終わりです。とりあえずwebアプリに必要な基礎のきと言った感じでしょうか。次巻ではより実践的な書き方やデータベースとの接続などを取り上げるようです。

ここまでつらつら写経しながら書いてきましたが、自分でまとめ直すことで改めて整理できた気がします。

今まで色々とweb上のチュートリアル的なものを試しても続かなかったのですが、この本は続けることができてます。本当にわかりやすいので、とりあえずpythonで一個webアプリ作ってみたい人には本当におすすめです。

2巻もやったらまた書きたいと思います。ありがとうございました。