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

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

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

## Pythonのインストール
Pythonのインストールについては、個人的にはanyenvとpyenvを使ってするのが便利だと思います。やり方はこちらをご覧ください。
[anyenvインストール](https://qiita.com/SUZUKI_Masaya/items/51366be1510f7fde7cdf)
[pyenvインストール](https://qiita.com/SUZUKI_Masaya/items/7ba3fef9eb7c9603762a)
↓

```bash
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ディレクトリのようなものです。

```bash
# .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をインストール
```bash
pipenv install masonite-cli
```

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

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

```bash
craft new project

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

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

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

```
craft serve
```
![Screenshot from 2018-12-31 23-14-01.jpg](https://qiita-image-store.s3.amazonaws.com/0/295826/b6fdfd30-d559-352c-7400-d7e1828e9b62.jpeg)

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

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

### ディレクトリ構造
ほぼLaravelと同じだと思ってもらって大丈夫だと思います。

<details><summary>長いので折りたたみ</summary><div>

```
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
```
</div></details>

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

```python
from masonite.routes import Get, Post

ROUTES = [
    Get().route('/', 'WelcomeController@show').name('welcome'),
]
```
Laravelに慣れている人ならこの意味はすぐに理解できると思います。ルートにgetでアクセスした時に、WelcomeConstollerのshowメソッドを呼びだす、という意味です。

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

```python
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](https://docs.masoniteproject.com/the-basics/routing#http-verbs)

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

ROUTES = [
    Get().route('/', WelcomeController.show)
]
```
[HTTP Helpers](https://docs.masoniteproject.com/the-basics/routing#http-helpers)

```python
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
```python
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
```python
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の人は慣れたことだと思います。

```bash
# Laravelの場合
php artisan make:controller SomeController

# Railsの場合
bundle exec rails g controller Some

# Masoniteの場合
craft controller Some
```
project/app/http/controllers/SomeControllers.pyが自動生成されます。

```python
class SomeController:
    """SomeController
    """

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

```bash
# Railsの場合
bundle exec rails g scaffold_controller Some

# Masoniteの場合
craft controller Some -r
```
<details><summary>自動生成されたコントローラーは長いので折りたたみ</summary><div>

```python
""" 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
```
</div></details>

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

```python
def show(self):
    return view('some')
```
project/resources/templates/some.htmlが返されます。

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

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

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

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

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

コントローラー

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

ビュー

```html
<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](https://orator-orm.com)

```python
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が自動生成されました。

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

```python
from config.database import Model

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

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

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

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