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

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についてのエントリー書きます。。

Werkzeug の generate_hashed_password がストレッチングに対応していた(のを知らなかった件)

先日ふと Werkzeug のドキュメントを読み返していたら generate_hashed_ password がストレッチングに対応していることに気付きました。

対応されたのはいつからだろうと思い、リリースを調べたら Ver 0.9 (2013年6月) で追加されていますね(今は Ver0.9.6 )全然知らなかった(汗。。

まあ、気付かなかったのはライブラリをアップデートしても今までの文字列パターン( method$salt$hash )で使用出来ていたからですね(後方互換大事)。

このブログではパスワードハッシュに generate_hashed_ password で生成したものを使い、 check_password_hash で照合しています。とりあえずストレッチング仕様のパスワードハッシュに置き換えることにします( OS X 10.10.1 Python3.4.2 で確認)

In [1]: from werkzeug import generate_password_hash

In [2]: pw_hash = generate_password_hash('default')

In [3]: pw_hash
Out[3]: 'pbkdf2:sha1:1000$hldobvVq$cf3ea3d710ddf58609f0c8ee25630e70bca8fc76'

method$salt$hash が pbkdf2:method:iterations$salt$hash になっています。これがデフォルトのようです。
ちなみに照合はこうなります

In [4]: from werkzeug import check_password_hash

In [5]: check_password_hash(pw_hash, 'default')
Out[5]: True

In [6]: check_password_hash(pw_hash, 'defaultx')
Out[6]: False

ストレッチングについてはこちらが詳しいですね
本当は怖いパスワードの話(3/4) - @IT

ちなみに前回のエントリーはこちら
[Werkzeug][Flask]FlaskをSalted Passwordsに対応させる | aoshiman.org

Flaskのスニペット
Salted Passwords | Flask (A Python Microframework)


[Flask][Jinja2]同一テンプレート内で同じ名前のブロックを複数定義する方法

最近このブログの各エントリーで付与したタグの一覧ページを作成したのですが、その一覧ページにてサイドバーが邪魔に感じたので表示させないようにした際、少しハマったのでメモしておきます。

現在、メインページと各エントリページは2カラムで、細かく言うと、bootstrap2を使い、containerをspan9とspan3で区切ってそれぞれの下に {% block body %} と {% sidebar %} を置くようにしています。

サードバーを取り外す為には、span9とspan3を一本化する必要があるので、request.endpointで条件分岐をし、 タグ一覧ページにリクエストが来た時だけ、1カラムにするようにしました。

ただし、この際、 {% block body %} を複数置くとエラーになります。なぜエラーになるかはマニュアルに記載されています。

これはマニュアルにあるように、selfを使ってbodyを呼び出すことでエラーを回避出来ます。最終的にはこのようなテンプレートになっています

<div class="container">
        {% if request.endpoint == 'show_all_tags' %}
        <div class="row">
            <div class="span12">
                {% for category, message in get_flashed_messages(with_categories=true) -%}
                <div class="{{category}}">{{message}}</div>
                {%- endfor %}
                {% block body %}{% endblock %}
            </div>
        </div>
        {% else %}
        <div class="row">
            <div class="span9">
                {% for category, message in get_flashed_messages(with_categories=true) -%}
                <div class="{{category}}">{{message}}</div>
                {%- endfor %}
                {{ self.body() }}
            </div>
            <div class="span3">
                <div id="sidebar">
                    {% block sidebar %}{% endblock %}
                </div>
            </div>
        </div>

jinjaの日本語マニュアルは2年前くらいに初めて読んで、ここらは俺にとっては縁がない箇所だろうなって思っていましたが、ようやく使うことになりました。


[Flask][SQLAlchemy]ブログのタグ付けされた件数を取得する

このブログは下記のようなmany to many relationshipで構成されているのですが、タグに紐ついたエントリー件数をサイトバーのタグ一覧に表示させるというタスクをようやくやっつけたのでメモっておきます(Python2.7.3で確認しています)

$ sqlite3 -header flasklog/flasklog.db ".schema"
CREATE TABLE entries (
        id INTEGER NOT NULL,
        title VARCHAR(60),
        text VARCHAR,
        pub_date DATETIME,
        PRIMARY KEY (id)
);
CREATE TABLE entry_tags (
        tag_id INTEGER,
        entry_id INTEGER,
        FOREIGN KEY(tag_id) REFERENCES tags (id),
        FOREIGN KEY(entry_id) REFERENCES entries (id)
);
CREATE TABLE tags (
        id INTEGER NOT NULL,
        name VARCHAR,
        PRIMARY KEY (id)
);

ここらへんを参考にpython - SQLAlchemy ordering by count on a many to many relationship - Stack Overflow:

In [26]: from flasklog.models import Base, Entry, Tag, entry_tags

In [27]: from flasklog.database import db_session

In [28]: from sqlalchemy.sql import functions as func

In [29]: db_session.query(Tag, func.count(entry_tags.c.tag_id).label('total')).join(entry_tags).group_by(Tag).order_by('total DESC').all()
2013-07-28 20:48:51,083 INFO sqlalchemy.engine.base.Engine SELECT tags.id AS tags_id, tags.name AS tags_name, count(entry_tags.tag_id) AS total
FROM tags JOIN entry_tags ON tags.id = entry_tags.tag_id GROUP BY tags.id, tags.name ORDER BY total DESC
2013-07-28 20:48:51,083 INFO sqlalchemy.engine.base.Engine ()
Out[29]:
[(Tag(2, u'Python'), 19),
 (Tag(15, u'Flask'), 10),
 (Tag(6, u'Life'), 6),
 (Tag(5, u'Twitter'), 4),
 (Tag(41, u'Bottle'), 3),
 (Tag(1, u'blogmop'), 2),
 (Tag(3, u'Linode'), 2),
 (Tag(4, u'CentOS'), 2),
 (Tag(7, u'foursquare'), 2),
 (Tag(9, u'iPhone'), 2),
 (Tag(14, u'Shizuoka'), 2),
 (Tag(27, u'Google'), 2),
 (Tag(30, u'Ubuntu'), 2),
 (Tag(32, u'Alfred'), 2),
 (Tag(39, u'Shizuokapy'), 2),
 (Tag(8, u'AppleScript'), 1),
 (Tag(10, u'Dropbox'), 1),
 (Tag(11, u'OSX'), 1),
 (Tag(12, u'vim'), 1),
 (Tag(13, u'VPS'), 1),
 (Tag(17, u'DISQUS'), 1),
 (Tag(18, u'Atelier SANGO'), 1),
 (Tag(19, u'Git'), 1),
 (Tag(20, u'Dashboard'), 1),
 (Tag(21, u'Werkzeug'), 1),
 (Tag(22, u'iTunes'), 1),
 (Tag(23, u'iOS5'), 1),
 (Tag(24, u'Gmail'), 1),
 (Tag(25, u'hatena'), 1),
 (Tag(26, u'Chrome'), 1),
 (Tag(28, u'iPad'), 1),
 (Tag(29, u'Flipboard'), 1),
 (Tag(31, u'CotEditor'), 1),
 (Tag(33, u'Evernote'), 1),
 (Tag(34, u'Logwatch'), 1),
 (Tag(35, u'feedparser'), 1),
 (Tag(36, u'BeautifulSoup'), 1),
 (Tag(37, u'javascript'), 1),
 (Tag(38, u'bookmarklet'), 1),
 (Tag(40, u'virtualenv'), 1),
 (Tag(42, u'Bitbucket'), 1),
 (Tag(43, u'Jinja2'), 1),
 (Tag(44, u'SQLAlchemy'), 1)]

こんな感じで取得出来たので、あとはtemplateに展開していくだけですね

とりあえずわかったことは

  • sqliteはワンライナーで使う方が便利だと思った。zshの履歴に記憶されるし、パイプも使える。
  • 一般的なrelationshipなSQLAlchemyならばStack Overflowをチェックしていればある程度は解決できると再認識した。

[Flask][Jinja2]テンプレートファイルの整理整頓をした

このブログのtemplatesフォルダにあるテンプレートファイル(html)が随分前からゴチャゴチャしていて何とかしたかったのですが、やっと綺麗に出来ました。

とりあえずメインとなるテンプレートファイルから部品として切り離せるものを別ファイルにして、メインテンプレート内でインクルードさせました

参考にしてのはこちらの記事です

あとはvimの機能を利用してインデントをしっかりと付けて見返した時にイライラしないようにしました(これ大事)


PAGE TOP