モブプロな人たちのブログ

Web サービス開発しているエンジニアの日記です。Python 大好き Flask 大好き。たまに Swift で iOS ゲーム開発も。

Flask(Python) 標準の session と Flask-Session の違い

本日、同僚のエンジニアとハッカソンみたいなことをやってきました。

なかなか盛り上がったのですが、その中で「Flask 標準の session と Flask-Session ってどんな場面で使い分けるの?」をしっかり理解できていないと感じ、いろいろ勉強したのでまとめておきます。

なお、英語だと良記事が多いのですが、英語アレルギーの同僚のために日本語でまとめる、という裏テーマもあります。

f:id:kaorr_mob:20180101080410p:plain

標準の session について

早速ハマったのですが、こちら名前だけ見るとセッション管理に関する何かに見えます。(見えますよね・・・?)

しかし、一般的な Web アプリケーションでいうところのセッション管理とは若干意味合いが異なっています。

実際のコードとデータを見てもらうと分かりやすいのですが、

@app.route("/")
def top():
    session['foo'] = 'foo'
    return render_template('top.html')

前後は省略しますが、session[key] に対して何か値を入れてみましょう。

その後、ページにアクセスし、開発者ツールなどでクッキーを見てみましょう。

f:id:kaorr_mob:20180505230447p:plain

はい、session という名前のクッキーが出来ましたね。

この文字列を取り出して見ると、eyJmb28iOiJmb28ifQ.Dc9K2Q.wa2YaXF3ahbq1YmVdDp82y6jhRA となっています。

これは、base64 でエンコードされた文字列になっています。. は区切り文字になっており、1つ目に上記で設定した foo に関するデータが入っています。2つ目以降は値のチェック用です。

では、1つ目の文字列を base64 でデコードしてみましょう。その際、文字列の最後に == を付与します。base64 は4文字ずつ変換する仕様で、文字列の長さを4の倍数にするために、足りない部分を = で埋める必要があります。

import base64
base64.urlsafe_b64decode('eyJmb28iOiJmb28ifQ==')
#=> b'{"foo":"foo"}'

はい、設定した foo に関するデータが、そのまま出てきましたね。

ちなみに、上記は b'{"foo":"foo"}' と短いためこのような仕様ですが、これがある一定の長さを上回ると仕様が変わり、zlib での圧縮が追加されます。↓の部分で分岐します。

https://github.com/pallets/itsdangerous/blob/master/itsdangerous.py#L878

実際にやってみましょう。

@app.route("/")
def top():
    session['foo'] = 'foo' * 20
    return render_template('top.html')

そして、ページにアクセスし、クッキーを取得します。今回は、.eJyrVkrLz1eyApFkI6VaANY3HE8.Dc9QcQ.6WhEXofYJc2H-uOUvMeTeXnLL5s になりました。先頭に . が付与されていれば OK です。

import base64
import zlib
zlib.decompress(base64.urlsafe_b64decode('eJyrVkrLz1eyApFkI6VaANY3HE8='))
#=> b'{"foo":"foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"}'

はい、無事にデータを取得できましたね。

以上のことから、標準の session はクッキーに全ての値を入れているだけであることが分かります。

Flask-Session について

詳しい導入方法は公式サイトを見て頂くとして、今回は sqlalchemy で動作させたいと思います。PostgreSQL を使います。

設定を終え、ブラウザのクッキーをクリアした後にページにアクセスすると、以下の通り名前は session と同じですが、文字列の雰囲気が変わったことに気付きます。

f:id:kaorr_mob:20180505233824p:plain

では、セッションテーブルの様子も見てみましょう。デフォルトでは、sessions というテーブル名です。

まず、テーブル定義ですが、以下の通りです。

f:id:kaorr_mob:20180505234346p:plain

次に、レコードですが、以下の通りです。data がバイナリデータのため長すぎて切れていますが、雰囲気は感じ取って頂けるかと思います。

f:id:kaorr_mob:20180505234909p:plain

以上のことから、Flask-Session は一般的な Web アプリケーションでいうところのセッション管理の役割を果たすものであることが分かります。

おわりに

というわけで、かなりざっくりな感じになってしまいましたが、Flask 標準の session と Flask-Session の違いをまとめてみました。

私自身まだ理解し切れていない部分も多いため、「ここ違うよ!」といった箇所がありましたら、ご指摘頂けますと有り難いです。

「会社というモンスターが、僕たちを不幸にしているのかもしれない。」を読んだ感想など

最近話題の本を読んだので、感想などをまとめておきたいと思います。

会社というモンスターが、僕たちを不幸にしているのかもしれない。

会社というモンスターが、僕たちを不幸にしているのかもしれない。

内容を事細かに書いてしまうのはアレなのですが、1箇所だけ引用させていただきます!

よく見ておかないといけないのは、カイシャというモンスターのブランドやイメージではなく、生身の人間である代表取締役が本当に信頼できる人なのかどうか。

そこを間違えると、知らぬ間に搾取されていたり、仕事が全然楽しくなかったりしてしまうわけです。

コレは結構考えさせられました・・・。

最近はそうでもないんですが、前職の頃などを振り返ると「組織のために」生きていた時期がありました。ホント、よくよく考えると、組織のためってなんなんだろうって話ですよね。

社長は「会社のために」と言うでしょうし、事業部長は「事業部のために」と言うでしょうし、部長は「部のために」と言うでしょう。

ただ、「組織のために」と言いながら結局は「自分のため(自分のやりたいことをやるため、評価を上げるため、給与を上げるため)」なんですよね。

もちろん、社会のことだけを考えて行動している方もいるとは思いますが、そういった方はホントごくわずかで、多くの方は「組織のために」という大義名分を振りかざしながら自分のために行動をしているんだと思います。

そう考えると、引用した上記の言葉は分かりやすいですね。

例えば、社長が「会社のために」と言ったとき、その言葉をそのまま受け取るのではなく「あの社長のために」と置き換え、それでも自分の中で納得感を持てるのか。

あの事業部長のために」「あの部長のために」「あの課長のために」と、抽象的な組織ではなく生身の一個人に置き換えたとき、しっかりとした納得感を持てるのか。

そこで納得感を持てているうちは良いです。そのまま、その方向で進みましょう。

ただ、そこで「ん?」と感じる瞬間がきたら、それは自分のキャリアについてしっかりと考え直さないといけない時期がきたということだと思います。

おわりに

この本は、「自分がやりたいことは何なのか?」を改めて考えさせてくれるキッカケになりました。

ぜひ、多くの方(特に若い方で、会社について悩まれている方)に読んで欲しいと思います。

「BPStudy#125〜テスト駆動開発(TDD)の真髄」に参加してみて

先日、以下の勉強会に参加してきました。

まとめもあります。

社外の勉強会というと、以前に主催(というほど何もしてないけど)側で参加して以来でした。

学生時代は、大規模なイベントに足を運んで、そこで開催されているセミナーとかに参加したことがありましたが、アプリケーション開発のエンジニアとして働き出してから参加するのは初めてだったので「はてさてどんなものかしら?」という感じでした。

f:id:kaorr_mob:20180101080410p:plain

結果、めちゃくちゃ面白かったです! また機会があれば参加したいと思いました。

ただ同時に、よく Twitter などで登壇者側の人が言う「登壇しない立場で勉強会に何度も参加しても意味ない」みたいな言葉の意味も理解できました。

参加して楽しかった!」で終わることがほとんどのパターンですよね。それを実際の仕事に結びつけて価値を出すのって、かなり大変。

なので、今回の勉強会で得たものをしっかりと仕事なりプライベートプロダクトに結びつけることが出来るまでは、他の勉強会は良いかなぁという感じです。もしくは、LT 枠で登壇するか。

当日のメモ

当日、勢いでメモったものがあるので、改めてまとめておきます。未来の自分のために。

テスト駆動開発は自習(写経)に向いている

テスト駆動開発

テスト駆動開発

t_wada 先生が翻訳された本です。

本の中にはコードが大量に出てくるので、写経するのに向いていると説明されていました。

今読んでる本が読み終わり次第、購入して写経したいと考えています。

設計をせずにテストから書き始める方法「ではない」

よくある勘違いのようです。

TDD というと「とにかくテストを書き始めることからスタート!」のイメージがありましたが、そんなことはもちろんありません。ちゃんと設計はしますよ、という話。

たくさんテストを書くことになるが、まずは小さいところからクリアにしよう

結果的には多くのテストコードが必要になりますが、その最終形ばかりを見てウッとならずに、小さいところから進めていきましょう。

ひょっとすると実は一番難しいかもしれないのは、大きい問題を分解して、順番を決めて、着手すること

うーん。これは TDD の文脈だけではなく、仕事など全般に言えることですね。

大きい問題は、どこから手をつけて良いか分からなくなり、モチベーションが低下しがちですね。

大きい問題を大きいまま解決できる人なんてそうそういないので、しっかりと自分が理解できる大きさにまで分解して、進める必要がありますね。

最初に文章として書いて、それをリスト化していく

これは面白いやり方だなぁと思いました。

自分がその機能に求めること、そのテストに求めることをまずは普通の(箇条書きではない)文章で書いていく。

その後、その文章を分解してリスト化していく。そして、そのリストにそってテストを書いていく。

ぜひ実践したいです。

コアロジックから優先的にテストを書いていく

MVC モデルの V などは、テストを書く手間がかかる割りに、賞味期限が短い(ことが多い)のです。

全部のテスト書かなきゃ!」と思うことは多いでしょう。そして、全部書かないとなんか見栄えが悪い、ということもあるでしょう。

テストに理解のない人が周りに多いと、「なんでちゃんと全部書かないの?」と詰められることもあるかもしれません。

しかし、時間などのリソースは有限です。コストパフォーマンスの悪いテストコードをたくさん書くより、コストパフォーマンスの良いテストコードに集中していきましょう!

テストの内容を具体的にする(考える負荷を減らす)

「数値を文字列に変換する」ではなく「数値 1 を文字列 '1' に変換する」といったイメージです。

曖昧なゴールを設定すると、コードを書いているときに思考がブレてきて、考える負荷が上がってきます。

なので、より具体的なゴールに変更できないか、考え直すのが大事です。

意味のないテストが意味ありげに見えるのは問題

これは反省ポイントですね・・・。

なんとなく追加したテストとか、不必要に重複したテストとか、書いた本人は経緯を知っていても、他人は知らないことがほとんどです。

また、書いた本人でさえ、しばらくしたら忘れるものです。そうすると、そういったテストコードが、今度は全体の生産性を下げだします。

「このテスト、なんで追加したんだろう・・・? でも、あるから保守しないとなぁ・・・」となるのです。

自分の記憶が鮮明なうちに、不要なテストコードは大胆に削除しましょう!

プライベート Web サービス開発に向けて、ようやく重い腰が上がった・・・

表題の通りですが、昨年末頃にネタを思いつき、「うおおおおおお!」と燃えていた Web サービス開発熱。

しかし、年末年始のドタバタや、まぁその他色々あって、未着手のままズーーーっときてました。

「いや、マジでこのままじゃいけないな・・・」

と一人で反省し、今日ようやく重い腰が上がりました。

いや、そんな重い腰になるほどの規模のサービスではなくて、自分が楽になるためだけの小さいサービスなんですけどね。なぜか、フットワーク重くなってて、進められていませんでした。

環境大事

いやはやホント、環境って大事ですね。

今日はコメダ珈琲で開発していたのですが、自宅では何かと集中できないことが多く、程よく周囲がガヤガヤしている環境での集中力は我ながらビックリするほどのものでした。

このままの勢いで、今月中にリリースまでいきたい・・・!

Python で get や set メソッドよりも素のままの属性を使う

「Effective Python」を久しぶりに読み返して、当時は特に何も思わなかった箇所に引っかかったのでメモを残しておきますシリーズ第7弾。

Effective Python ―Pythonプログラムを改良する59項目

Effective Python ―Pythonプログラムを改良する59項目

Python で get や set メソッドよりも素のままの属性を使う

他の言語から Python に移ってきたプログラマは、ごく自然にクラスの中でゲッターやセッターメソッドを明示的に実装しようとする

ふむふむ、なるほど。確かに昔 Java を勉強してた時、ゲッターとかセッターとかちょくちょく書いてました。

class User1:
    def __init__(self, age):
        self.age = age

    def get_age(self):
        return self._age

    def set_age(self, age):
        self._age = age


if __name__ == '__main__':
    user1 = User1(30)
    user1.set_age(user1.get_age() + 1)

↑こんな感じのやつです。

ただ、見てわかる通り、age に 1 を足したいだけなのに、やけにゴチャゴチャした感じになってますね。

こういうのは「Python 流ではない」みたいです。

では、どうするかというと、

class User2:
    def __init__(self, age):
        self.age = age


if __name__ == '__main__':
    user2 = User2(30)
    user2.age += 1

まあ簡単。

いや、ふざけてないです。本当に必要なのか考えずに、おまじない的にゲッターやセッターを書くのはやめましょうという話ですね。

さて、しばらくして特別な振る舞いが必要になった時どうすれば良いんでしょうか?

例えば、年齢は200歳まで許容するが、それ以上は例外処理にしたい場合、

class User3:
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, age):
        if age > 200:
            raise ValueError('age must be <= 200')
        self._age = age


if __name__ == '__main__':
    user3 = User3(30)
    user3.age += 1

    user3.age += 170
    # ValueError: age must be <= 200

デコレータを付けました。これで OK です。