Flask タグの一覧です(17 件)

探検! Python Flask を読んだ

探検! Python Flask
Robert Picard, 濱野 司(訳)
達人出版会
発行日: 2015-05-08
対応フォーマット: PDF, EPUB

昨日この本を見つけて、速効で買いました。フォーマットがePubだったので、mobiに変換してKindleに取り込んで読んでいます。なんか一年前に出てたみたいですね、全然知らんかった(;一_一)

このブログをFlaskを使って作り直したのが2011年9月なので、Flaskはかれこれ5年程触っていますが、未だに好きなフレームワークです。今まで日本語のFlask本は(多分)出ておらず、使用にあたっては、チュートリアルやったり、Flaskのリポジトリのexampleを弄ってみたり、トラブルはStackOverflowで解決図ったりしてのらりくらりなんとか使ってきましたが、やはり日本語のFlask解説本、あると便利ですね。

どんなことが書かれているかはページサンプルを見て頂ければと思いますが、個人的にはBlueprintsの解説がしっかりと書かれていたので嬉しかったです。プロダクトでも社内管理システムでもFlaskで作ろうとするならばBlueprints無いと大変だと思うので。

使ったことがないエクステンションが幾つかあったので時間があったら勉強がてら、このブログに採用してみたいなと思いました。


Frozen-Flask で build 後にHTTPサーバを実行させる

Frozen-Flaskで静的ファイルの生成を実施する場合、このようなスクリプトファイルを作って実行します

# freeze.py
from flask_frozen import Freezer
from app import app

freezer = Freezer(app)

if __name__ == '__main__':
    freezer.freeze()
実行するとbuildというフォルダが作成され、静的ファイル群はその中に生成されます。本番サーバにデプロイする場合は、build内のファイル郡をドキュメントルート等にアップロードします。
デプロイする前に生成された静的ファイルを確認する場合、今までは

$ cd build
$ python -m http.server
などとしていたのですが、freezer.run() を使えば、ビルド後にHTTPサーバ(実態はwerkzeugだと思う)が起動されることがドキュメントに書いてありました。

Frozen-Flask — Frozen-Flask 0.12 documentation

なので今は、引数なしfreeze.pyでビルド、 引数run付きで ビルド後にHTTPサーバ起動というようにしています。

import sys
from flask_frozen import Freezer
from app import app

freezer = Freezer(app)

if __name__ == '__main__':
    if len(sys.argv) > 1 and sys.argv[1] == "run":
        freezer.run(debug=True)
    else:
        freezer.freeze()

CMSやブログ的にFlaskを使っているならば、Frozen-Flaskはオススメです。また、静的サイトジェネレーター的に使う場合は下記サイトが参考になります。

Dead easy yet powerful static website generator with Flask | Code | Nicolas Perriault

Flaskアプリケーションに二段階認証機能を実装(通算2回目)

Flask製ブログツールをローカルではなく、パブリックなVM(Digitalocean)に立ててブログを書くことにしたので、念の為、二段階認証機能を実装しました。
実は、ブログに組み入れるのは2回目で、ちょうど2年前の11月に一度実装しています(その後面倒くさくなってやめた)。その時の学んだことは Shizuoka.py#3 で発表させて頂きました(なつかしい)。


当時は二段階認証の名前は聞いたことがあるけど実際にやったことがある人が少なく(Shizuoka.py#3の参加者でも使っている人が10人中1人(私)だった)、発表聴いてくれている方々が理解してくれているかどうか不安でした。それから2年経ち、多くのサービスで利用されるようになってきたので、今現在は実際に使うようになっている方も多いのではないかと思います。

pyotpを使う

pythonでtotpを扱うモジュールはいくつかありますが、私はpyotpを使用しています。シンプルなので気に入っています。 基本動作をIPyhtonで確認してみます

In [1]: import pyotp

In [2]: import time

In [3]: totp = pyotp.TOTP('JBSWY3DPEHPK3PXP')

In [4]: totp.now()
Out[4]: '211775'

In [5]: totp.verify(211775)
Out[5]: True

In [6]: time.sleep(30)

In [7]: totp.verify(211775)
Out[7]: False

因みに上記で記述されているシークレットキー(JBSWY3DPEHPK3PXP)はgoogle authenticatorのサンプルコードでも記述されていて、totpのサンプルで使われるのを時々見かけますが、これはBASE32文字列におけるHexspeak(deadbeef)になっています。その為、サンプルで使われているのかと思慮。

In [1]: import base64

In [2]: import binascii

In [3]: binascii.b2a_hex(base64.b32decode('JBSWY3DPEHPK3PXP'))
Out[3]: b'48656c6c6f21deadbeef'

Flaskアプリへ組み込む

Flaskアプリへ組み込む場合、べた書きしてもよいのですが、再利用できるように、pyotpを使用して受け取ったワンタイムパスワードが正しいかどうか判定するfunctionを作成します。こんな感じです。

from pyotp import TOTP
from datetime import datetime, timedelta

def tfa_auth(secret, otp):
    totp = TOTP(secret)
    now = datetime.now()
    past = now + timedelta(seconds=-30)
    try:
        return otp == totp.now() or otp == totp.at(past)
    except:
        return False

タイムギャップ処理(-30)は必ずつけるのですが、+30をどうするのか悩みます(最近のスマートフォンは必ず時刻同期されているだろうから必要ない?)。
そしてviews側のfunctionはこんなかんじです。この処理の前にユーザー認証処理があって、認証されたらこのfunctonに飛びます。

@app.route('/verify_tfa/', methods=['GET','POST'])
def verify_tfa():
    error = None
    if request.method == 'POST':
        if tfa_auth(app.config['TFA_SECRET'], str(request.form['token'])):
            session['logged_in'] = True
            flash(u'You were logged in', 'alert-success')
            return redirect(url_for('show_entries'))
        else:
            error = 'Invalid verification code'
    return render_template('verify_tfa.html', error = error)

二段階認証機能の実装は、QRコード生成等のステップが無ければ、結構単純なので一度やってみるのもいいと思います。


[jQuery] timeago.js を使って経過時間表示させる

Frozen-Flaskを使うようになり、今までサーバーサイドで表示させていた 経過時間表示が使い物にならなくなったので、クライアントサイドで表示させるように変更しました。具体的には timeago.js を使って実現しています。

timeago: a jQuery plugin

やり方ですが、まず app.py 側で該当エントリーの 日時(この場合は pub_date) を strftime を使ってISO8601に変換

@app.route('/entry/<int:post_id>/')
def show_entry(post_id):
    dt = Entry.query.filter_by(id = post_id).one()
    flash(u'%s' % dt.pub_date.strftime('%Y-%m-%dT%H:%M:%S+09:00'), 'alert-success')
    return render_template('show.html',
        entries = Entry.query.filter_by(id = post_id).all())

次に templateファイルl内 で flashメッセージを受け取る記述をします。その時に spanタグの class名をtimeagoとし、titleに日時(ISO8601)がセットされるようにします。spanタグはabbrでも可能ですが、後述するtimeago.js のパラメータの記述も合わせる必要があります。

{% elif request.endpoint == 'show_entry' %}
                <div class="row">
                    <div class="col-md-8">
                        {% for category, message in get_flashed_messages(with_categories=true) -%}
                        <div class="{{category}}"><span class=timeago title="{{message}}"></span></div>
                        {%- endfor %}
                        {{ self.body() }}
                    </div>
                    <div class="col-md-4">
                        <div id="sidebar">
                            {% block sidebar %}{% endblock %}
                        </div>
                    </div>
                </div>

最後に同じくtemplateファイルで timeago.js の設置と設定をします。

<script type="text/javascript" src="/static/js/jquery.timeago.js"></script>
        <script>
            // Japanese setting for Timeago
            $.timeago.settings.strings = {
                prefixAgo: "",
                prefixFromNow: "今から",
                suffixAgo: "",
                suffixFromNow: "後",
                seconds: "1 分未満",
                minute: "約 1 分",
                minutes: "%d 分",
                hour: "約 1 時間",
                hours: "約 %d 時間",
                day: "約 1 日",
                days: "約 %d 日",
                month: "約 1 月",
                months: "約 %d 月",
                year: "約 1 年",
                years: "約 %d 年",
                wordSeparator: ""
            };
        </script>
        <script>
            $(function() {
                $('span.timeago').timeago();
                $('span.timeago').before("この記事は投稿から");
                $('span.timeago').append("が経過しています");
            });
        </script>

私の場合、前後にメッセージを結合したかったので、before とappend で追加しています(このやり方でいいのか不安)。因みに日本語化はこちらを参考にしました。
jquery-timeago/locales at master · rmm5t/jquery-timeago

尚、今までのサーバーサイド側での方法はこちらのエントリーをご参照願います。
[Python][Flask]ブログエントリーの経過時間を表示させる | aoshiman.org


Frozen-Flask + wercker + Amazon S3 でFlask製ブログを静的サイトとして運用してみた

そんなに大それた話ではないのですが、数日前にFrozen-Flaskを使ってFlask製ブログを文字通りFrozenさせてAmazon S3にホスティングさせてみました。なんとかうまくいっているので暫くこれでやってみます。


Frozen-Flaskを使おうと思った経緯


以前、Flaskの作者Arminさんがこのように言っていたのですが、自分の中でFlaskは mod_wsgi や uWSGI、Gunicorn などと組み合わせて使うもので、当初、Frozen-Flask が必要となる場面をなかなか想像出来ませんでした。
ただその後、WordPressで作成していた旧ブログのホスティングで悩んでいた時にStaticPressを試してみたことがあり、ブログやCMSならば、静的サイト構築もありなんだなと思った経緯があります。

今回Frozen-Flaskを使おうと思ったのは

  • Shizuoka.py界隈でWordPressからPelicanへ移行した方がいて、静的サイトもいいなあと思い始めていた
  • 常々VPS(さくらのVPS 2G)のランニングコストをもう少し節約しようと思っていた

という理由からだったのですが、静的サイトジェネレータへ引っ越すのではなく、4年近く使っているこのブログは生かしたいなと。ならばFrozen-Flaskやってみるかという流れです

あと、Amazon S3を選んだのはコストダウンとwerckerとの組み合わせ事例が幾つかあって導入しやすかったからです。
それとwerckerの導入(静的ファイルのビルドとAmazon S3 へのデプロイ)なのですが、割とすんなり出来たのでローカルでブログエントリーを書いてリモートリポジトリにPushすればデプロイまでやってくれる状態までもっていけました。werckerスゲー便利です。


Frozen-Flaskの運用方法

実際の運用なのですが、現状はこのように使っています。

  1. OS X Yosemite の virtualenv(Python3.4)環境で $python app.py でブログ起動
  2. エントリーを書いて コミットし、BitbucketのプライベートリポジトリへPush(sqliteのDBファイルも含みます)
  3. werckerがフックを感知して、リポジトリをclone
  4. werckerがwercker.ymlの記述通りに環境構築(virtualenvやpip install -r requirements.txtなど)
  5. werckerが$python freeze.py して静的ファイルをビルド
  6. werckerが出来上がった build/ の中身をAmazon S3へsyncする

もうFlask製ブログはローカルエディタみたいなものですね。後は全てwercker任せです。
因みに構成はこの時からほとんど変更ありません
[Python][Flask][Sqlalchemy]blogを作りなおしてみた | aoshiman.org

また、wercker.ymlの中身はこんな感じです。boxはwerckerが用意している標準のものです

box: wercker/python
# Build definition
build:
  # The steps that will be executed on build
  steps:
    # Use this virtualenv step for python 3.3
    - virtualenv:
        name: setup virtual environment
        python_location: /usr/bin/python3.3

    # A step that executes `pip install` command
    - pip-install:
        requirements_file: "requirements.txt" # Optional argument.

    # A custom script step, name value is used in the UI
    # and the code value contains the command that get executed
    - script:
        name: make html files
        code: |
            python freeze.py
# Deploy definition
deploy:
  steps:
      - s3sync:
          key_id: $KEY
          key_secret: $SECRET
          bucket_url: $URL
          source_dir: build/


Frozen-Flaskでつまずいた箇所

  • URLルールをtrailing slash付きに変更
    Frozen-Flask は app.route("/hoge" ) のようなるルールの場合、 hoge というHTMLファイルを作り、 app.route("/hoge/") のような場合、hogeというディレクトリを作り、中に index.html を作ります。
    Flaskで動的に動かしていた時は前者のようなtrailing slash無しのURLルールだったのですが、Pagenateを構築している関係か、Frozen-Flaskでのビルドが途中で止まって上手く行かず。
    色々調べてみたのですが、結局、trailing slashを付けたほうが手数を掛けずにビルド出来るので、URLルールをすべてtrailing slash付きに変更しました。 現在このブログのURLで検索エンジンにインディクスされていたり、ブックマークされているのはtrailing slash無しが多いのですが、適切にリダイレクトされるので、はてなブックマーク等のURLが分かれてしまうところは残念ですが、それ以外は問題ないです。

  • errorhandler() デコレーターは無視される
    ルーティングでのエラー処理で、ドキュメントに書いてあるerrorhandler()デコレータを使っているのですが、 これがFrozen-Flaskでは無視されます。なので別途静的ファイル作成用View関数を用意しました。 こんな感じのやつです

    @app.route('/404.html')
    def error404():
        """function of 404 error handler for frozen"""
        return render_template('404.html')
    

  • Flask-DebugToolbarは外しておく
    Deboug-Toolbarが有効な状態でfreezeさせるとエラーで止まってしまいました。今は必要ないので外しました

残処理について

  • Message Flashingを使って時間経過を表示させているのですが、サーバサイドで処理しているので、ビルドした時間での経過表示で静的ファイルが構築されてしまいます。さすがに毎日ビルドしなおすわけにはいかないので、クライアントサイドで経過表示出来るようにしたいです。
    4月4日 timeago.js をベースにローカライズ含め時間経過を再実装しました。
  • もう少し詳しいFrozen-Flaskについてのエントリー書きます。。

PAGE TOP