Python + FlaskでMVC的なことをやってみた

Python + FlaskでMVC的なサンプルをつくったメモ。

MVC的な記事まとめ

ひとまずMVCについて言及されている記事をまとめ。
MVCはWikipediaが詳しい。
以下は調べた中でもわりと個人的にしっくりきた記事たち。

Web Applicationを綺麗に設計するためのMVACという考え方 - Dive into the Tech World!
Re: @kazuho: handlersocket plugin や mycached を使えば memcached は不要か、それとも使うべきケースがあるか。考察せよ [10点] - blog.nomadscafe.jp
Life is beautiful: Ruby on Railsの「えせMVC」の弊害
the { buckblogs :here }: Skinny Controller, Fat Model

共通して言われている気がするのが下記2点。守れば比較的キレイに設計できるかも。

  • コントローラに処理を書きすぎない
  • モデル = ORM としない

これはやっとけみたいなことが多いわりに絶対的なお約束がないMVC。
これはやっとけを全部守ってたらカオスになりそうなのでこの2点。

ModelでDBI, ORM, …という各機能をラップしてあげるイメージが一番しっくりきた。
これでごちゃごちゃしたソースコードが少しはスッキリしてくれるとありがたい。

Python + FlaskでMVC的なアプリ

上記記事を参考にしながらPython+FlaskでつくったMVC的構造のサンプルアプリをペタリ。

https://github.com/utahta/Flask-MVC-Pattern

projects/create で何かしらを登録し、projects/ で登録した何かしらを表示する至極簡素な機能付き。

以下、ソースコード(抜粋)。

コントローラ(myapp/controller/projects.py)でルーティング&入力値チェック。

from flask import Module, render_template, redirect, url_for, request  
from flaskext.wtf import Form, TextField, validators  
from myapp.model.projects import ProjectsModel  
  
projects = Module(__name__)  
  
@projects.route('/')  
def index():  
    model = ProjectsModel()  
    # リスト表示  
    return render_template('projects/index.html', model=model)  
  
class CreateForm(Form):  
    """  
    /projects/create 用のフォームクラス  
    入力値チェックも担う  
    """  
    title = TextField(u'タイトル', [validators.Length(min=1, max=25)])  
    description = TextField(u'説明', [validators.Length(min=1, max=100)])  
  
@projects.route('/create', methods=("GET", "POST"))  
def create():  
    model = ProjectsModel()  
    form = CreateForm(request.form)  
    if request.method == 'POST' and form.validate():  
        # チェック通ったらデータを保存し、/projects へリダイレクト  
        model.save(form.title.data, form.description.data)  
        return redirect(url_for('projects.index'))  
    # 作成画面を表示  
    return render_template('projects/create.html', form=form)  

Flask-WTFというFlaskの拡張機能を使ってフォームの値をチェック。
class CreateForm(Form) の部分。
モデル側に定義しようか一瞬迷うも、/createでしか使わないことを考えコントローラ側に定義。
プログラムがもっと大きくなったらまた考える。

モデル(myapp/model/projects.py)でデータ取得&保存。

from myapp import db  
from myapp.db.orm.projects import Projects  
  
class ProjectsModel(object):  
    def get_entries(self):  
        return db.session.query(Projects).all()  
      
    def save(self, title, description):  
        db.session.add(Projects(title, description))  
        db.session.commit()  

ORMオブジェクトのProjectsをProjectsModelが操作。ラップップ。
ORMはSQLAlchemyを使用。
仕組みが単純なのでやってることも単純。
プログラムがもっと大きくなっても、データの一貫性保証とかもろもろここで処理する。

設計はデザイン

デザインはセンス。センスは経験。
ということで、以前つくったウェブアプリをFlaskで書き直して経験値ためる。
ついでにGAEに移植できたらいいかしらん。