Python
PHP
laravel
masonite

LaravelそっくりなPython製WebフレームワークMasoniteの紹介

2019年あけましておめでとうございます!!!
私は今年はPHPの大人気フレームワークLaravelそっくりのPython製フレームワークであるMasoniteを広めていきたいなーと考えています。Laravelの素晴らしいディレクトリ構造、フルスタックFWのパワフルさ、そしてPythonの可読性の高さ、書きやすさのミックスされたフレームワークは使いやすいこと間違いなし!

ということで第一弾、Masoniteフレームワークの紹介!まずは環境構築から!

Pythonのインストール

Pythonのインストールについては、個人的にはanyenvとpyenvを使ってするのが便利だと思います。やり方はこちらをご覧ください。
anyenvインストール
pyenvインストール

anyenv install pyenv
exec $SHELL -l # bashを更新

pyenv install 3.6.6 # Python3.6.6をインストール
pyenv local 3.6.6 # ディレクトリ内でPythonへのパスを3.6.6へ通すように設定する

venv環境を作る

venvとは、プロジェクト毎で使うPythonパッケージを、ローカルに隠蔽するためのものです。
JavaScriptで言うnode_modules、Laravel、Railsのvendorディレクトリのようなものです。

# .venvにvenv環境のディレクトリを作る
python -m venv .venv

# venvを有効化
source .venv/bin/activate

プロジェクトで使用するPythonパッケージの管理には、pipenvを使います。pipfile、pipfile.lockというファイルにPythonパッケージの一覧を記録して、それを介して複数の開発者が同じパッケージを楽にインストールできるようにする仕組みです。JavaScriptのpackage.json、RubyのGemfileと同じような仕組みのものです。

# pipenvをインストール
pip install pipenv

これでpipenvコマンドが使えるようになります。

Masoniteをインストール

pipenv install masonite-cli

masonite-cliをインストールすると、craftコマンドが使えるようになります。このコマンドを使うことで、php artisanコマンドや、railsコマンドのように、コントローラーを作ったりモデルを作ったり開発サーバーを起動したりができます。

プロジェクトを作る

craft newコマンドで、プロジェクトを作ります。例ではプロジェクト名をprojectとしています。ここは任意に好きな名前を付けてください。

craft new project

# projectディレクトリ以下に移動
cd project

# Masoniteが使うパッケージ類をインストール
craft install

これでプロジェクトを起動する準備が整いました!serveコマンドでブラウザから見てみましょう。

craft serve

Screenshot from 2018-12-31 23-14-01.jpg

いかがでしょうか。 Laravelそっくりですね笑

Masoniteフレームワークの紹介

ではここからは、フレームワーク自体について紹介していきます。
PythonのウェブフレームワークといえばDjangoが有名ですが、DjangoはMTV構造なのに対して、Masoniteは完全にLaravel、Railsを同じMVC構造を持っています。
また、モデルに関してはLaravelを同じ考え方を採用していて、Railsがmodelsディレクトリがあるのに対して、Masoniteにもmodelsディレクトリはありません。もちろんmodelsディレクトリを作る使い方もできます。

ディレクトリ構造

ほぼLaravelと同じだと思ってもらって大丈夫だと思います。

長いので折りたたみ
project
  app
    http
      controllers
        WelcomeController.py
      middleware
        AuthenticationMiddleware.py
        CsrfMiddleware.py
        LoadUserMiddleware.py
        VerifyEmailMiddleware.py
    providers
    User.py
  bootstrap
  config
    application.py
    auth.py
    broadcast.py
    cache.py
    database.py
    mail.py
    middleware.py
    packages.py
    providers.py
    queue.py
    session.py
    storage.py
  database
    migrations
    seeds
      database_seeder.py
      user_table_seeder.py
  resources
    templates
      welcome.html
  routes
    web.py
  storage
    compiled
      style.css
    public
    static
      sass
        style.scss
    uploads
  tests
    feature
      test_feature_works.py
      test_imports.py
    unit
      test_works.py
  .env
  requirement.txt
  wsgi.py

ルーティング

ルーティングはproject/routes/web.pyに書きます。
Djangoではgetとpostなどはルーティングで切り替えられませんが、MasoniteではHTTP動詞に対応してルートを定義できるようにしています。

from masonite.routes import Get, Post

ROUTES = [
    Get().route('/', 'WelcomeController@show').name('welcome'),
]

Laravelに慣れている人ならこの意味はすぐに理解できると思います。ルートにgetでアクセスした時に、WelcomeConstollerのshowメソッドを呼びだす、という意味です。

また、クラスとメソッドを文字列でハードコーティングしたくない!という人のために、コントローラーのクラスとメソッドをimportして使うこともできます。こちらはよりDjangoのurls.pyと近い感覚で使えると思います。

from masonite.routes import Get, Post
from app.http.controllers.WelcomeController import WelcomeController

ROUTES = [
    Get().route('/', WelcomeController.show)
]

ルートオプション

http動詞の指定には、関数型っぽく書けるmasonite.routesと、省略形のメソッドであるmasonite.helpers.routesの2つから選べます

HTTP Verbs

from masonite.routes import Get
from app.http.controllers.WelcomeController import WelcomeController

ROUTES = [
    Get().route('/', WelcomeController.show)
]

HTTP Helpers

from masonite.helpers.routes import get
from app.http.controllers.WelcomeController import WelcomeController

ROUTES = [
    get('/', WelcomeController.show)
]

ルーティンググループ

さらにルーティングにグループを設定することができます。Djangoのルーティンググループの作り方と比較してみましょう。

hoge/aaaにアクセス→aaaメソッドが呼ばれる
hoge/bbbにアクセス→bbbメソッドが呼ばれる
hoge/cccにアクセス→cccメソッドが呼ばれる

Django
from django.urls import include, path
from . import views

hoge_paterns = [
    path('aaa', views.hoge_views.aaa),
    path('bbb', views.hoge_views.bbb),
    path('ccc', views.hoge_views.ccc)
]

urlpatterns = [
    path('hoge/', include(hoge_paterns))
]
Masonite
from masonite.helpers.routes import get
from app.http.controllers.HogeController import HogeController

ROUTES = [
    RouteGroup([
        get('/aaa', HogeController.aaa),
        get('/bbb', HogeController.bbb),
        get('/ccc', HogeController.ccc),
    ], prefix='/hoge')
]

コントローラー

ではcraftコマンドを使って、コントローラーを作ってみましょう。Djangoの人はこれは初体験、LaravelとRailsの人は慣れたことだと思います。

# Laravelの場合
php artisan make:controller SomeController

# Railsの場合
bundle exec rails g controller Some

# Masoniteの場合
craft controller Some

project/app/http/controllers/SomeControllers.pyが自動生成されます。

class SomeController:
    """SomeController
    """

    def show(self):
        pass

次のように、コマンドに「-r」のコマンド引数を追加することで、CRUDに対応したアクションメソッドが自動生成されます。Railsのscaffold_controllerと似ていますね。

# Railsの場合
bundle exec rails g scaffold_controller Some

# Masoniteの場合
craft controller Some -r

自動生成されたコントローラーは長いので折りたたみ
""" A SomeController Module """


class SomeController:
    """Class Docstring Description
    """

    def show(self):
        """Show several resource listings
        """

        pass

    def index(self):
        """Show a single resource listing
        """

        pass

    def create(self):
        """Show form to create new resource listings
        """

        pass

    def store(self):
        """Create a new resource listing
        """

        pass

    def edit(self):
        """Show form to edit an existing resource listing
        """

        pass

    def update(self):
        """Edit an existing resource listing
        """

        pass

    def destroy(self):
        """Delete an existing resource listing
        """

        pass

ビューを返すにはviewメソッドをreturnします

def show(self):
    return view('some')

project/resources/templates/some.htmlが返されます。

また、dict型をreturnすると、Jsonを返すことができます。

def show(self):
    return {'key': 'value'}

ビュー

ビューもコマンドで作ることが出来ます。

craft view some

project/resources/templates/some.htmlが自動生成されます。

テンプレートエンジンにはJinja2が使われています。これはFlaskでの開発で人気がありますね。
コントローラーでviewメソッドの第二引数にdict型を設定して、ビューに変数を渡します。

コントローラー

def show(self):
    return view('some', {'world': 'world'})

ビュー

<p>Hello {{ world }}</p>

モデル

テーブルを作るには、craftコマンドを使って、始めにマイグレーションファイルを作ります。
次のようなツイッターのpostのような簡単な構造のテーブルを作ってみましょう。

postテーブル

英語名 日本語名 null許可 説明 外部キー
id id int x --- ---
post 投稿 text x --- ---
user_id ユーザーID int x 投稿者のID User.id
timestamp タイムスタンプ int x 投稿日時 ---
craft migration create_post_table --create posts

project/databases/migrations/2018_12_31_185839_create_post_table.pyが自動生成されます。

ではここにテーブル構造を作っていきましょう。
Masoniteでは、データベース周りは全て、OratorというLaravelのORMライクなアクティブレコードベースのPythonのライブラリを採用しています。マーグレーションファイルの書き方も、ORMも全てOratorの書き方に従います。これは一部Flaskで使っている人もいるようですが…
Orator

from orator.migrations import Migration

class CreatePostTable(Migration):

    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create('posts') as table:
            table.increments('id')
            table.long_text('post')

            table.integer('user_id').unsigned()
            table.foreign('user_id').references('id').on('users')

            table.timestamps()

    def down(self):
        """
        Revert the migrations.
        """
        self.schema.drop('posts')

そしてcraftコマンドでマイグレートします

craft migrate

マイグレートできたら、craftコマンドを使ってモデルを作ります。この時に「models/」を含めることで、modelsディレクトリも一緒に作られます。

craft model models/Post

project/app/models/Post.pyが自動生成されました。

このモデルの中では、バリデーションルールや、テーブルからデータをやり取りする純粋なビジネスロジックを書き、コントローラーから呼び出すようにします。

from config.database import Model

class Post(Model):
    __fillable__ = ['post', 'user_id']

まとめ

以上でMasoniteフレームワークの紹介を終わります。

公式ドキュメントは全て英語で書かれていますが、とても簡単な英語で書かれているのでぜひ挑戦してみてください。また、簡単なチュートリアルも載っています。
公式ドキュメント

いかがでしたでしょうか、今までLaravelを使っていた人も、Railsを使っていた人も、これを機会にPythonでウェブを作るということを検討してみても面白いと思います。
Pythonを実行するインタープリターを、普通のPythonではなくPypyを使うことで、全く同じソースコードのままGo言語の80%ほどの速度が出せるほど高速化することもできます。
Pythonでウェブを作り、ゲームを作り、ドローンを飛ばし、データ解析をし、機械学習をする。Pythonは本当に簡単に書けて、簡単に読めて、幅広く使える、素晴らしいプログラミング言語だと感じています。
次回はLaravelとMasoniteで全く同じものを同時進行で作り、2つのフレームワークを徹底比較してみたいと思います。こうご期待!