モダンPython WebフレームワークMasonite徹底解説 🚀

Python

開発者中心設計の「Batteries Included」フレームワーク

Masoniteとは? 🤔

Masoniteは、開発者の体験を重視して設計された、比較的新しいPythonのWebフレームワークです。2017年12月頃に開発が始まり、2018年1月にバージョン1.0がリリースされました。Djangoのような「Batteries Included(全部入り)」のアプローチを取りつつも、よりモダンで、Ruby on RailsやPHPのLaravelといった他の言語の人気フレームワークから影響を受けた設計思想(規約重視、開発効率の高さなど)を取り入れているのが特徴です。

PythonのWebフレームワーク界隈では、長らくフルスタックのDjangoとマイクロフレームワークのFlaskが主流でしたが、Masoniteはその中間に位置し、両者の利点を組み合わせることを目指しています。特に、開発のしやすさ、学習の容易さ、そして拡張性の高さを重視しており、初心者から経験豊富な開発者まで幅広い層に対応することを目指しています。

開発者のJoseph Mancuso氏によって始められ、コミュニティと共に急速に進化を続けています。2021年12月には、内部構造を大幅に刷新したバージョン4.0がリリースされました。

Masoniteの主な特徴 ✨

  • モダンな設計思想: Ruby on RailsやLaravelなど、他の言語で成功しているフレームワークの良い点を取り入れています。規約に基づいた開発スタイルにより、定型的な作業を減らし、本質的な開発に集中できます。
  • MVCアーキテクチャ: Model-View-Controllerパターンを採用し、コードの関心事を分離しやすく、整理された構造を保てます。
  • サービスコンテナと依存性注入 (IoC/DI): クラス間の依存関係を自動的に解決する強力なサービスコンテナを備えています。これにより、コードの結合度が下がり、テストや機能の差し替えが容易になります。
  • Masonite ORM: LaravelのEloquent ORMにインスパイアされた、Active Recordパターンの美しいORM(オブジェクトリレーショナルマッパー)を提供します。元々はOrator ORMを使用していましたが、開発が停滞したため、Masonite 3.0 (2021年1月リリース) からは独自開発のMasonite ORMに移行しました。MySQL, PostgreSQL, SQLiteなどをサポートしています。
  • Craftコマンドラインツール: プロジェクトの作成、コントローラーやモデルの生成、マイグレーションの実行、開発サーバーの起動など、開発を効率化するための強力なコマンドラインインターフェース (`craft`) を提供します。
  • ルーティング: シンプルで表現力豊かなルーティングエンジンを持ち、HTTPメソッドに基づいたルート定義や名前付きルートなどが簡単に設定できます。
  • テンプレートエンジン: デフォルトでJinja2を採用しており、Pythonライクな構文でHTMLテンプレートを記述できます。
  • サービスプロバイダ: フレームワークの機能を拡張したり、独自のサービスを登録したりするための仕組みです。これにより、Masoniteは非常に拡張性が高くなっています。
  • 組み込み機能: メール送信、キュー(非同期タスク処理)、タスクスケジューリング、通知、イベントリスナーなど、Webアプリケーション開発でよく必要とされる機能が標準で組み込まれています。
  • 開発者中心: ドキュメントの充実や、インストールからデプロイまでのプロセスを容易にすることに力を入れています。

インストールとプロジェクト作成 💻

Masoniteの利用を開始するのは非常に簡単です。

前提条件

  • Python (バージョン3.6以上、公式ドキュメントでは3.11以下を推奨している場合あり)
  • pip (Pythonのパッケージインストーラ)
  • OpenSSL (最新版推奨)
  • (Linuxの場合) Python開発パッケージ (例: `python3-dev`, `python3.x-dev`) と `libssl-dev`

インストール手順

  1. Craft CLIのインストール:
    Masoniteプロジェクトの管理ツールである `craft` コマンドをインストールします。
    pip install masonite-cli
    または、Masonite本体を直接インストールすることもできます(craftも含まれます)。
    pip install masonite
    インストール後、`craft` コマンドが利用可能か確認します。
    craft
    利用可能なコマンドのリストが表示されれば成功です。
  2. 新規プロジェクトの作成:
    `craft new` コマンドで新しいMasoniteプロジェクトを作成します。`` は任意のプロジェクト名に置き換えてください。
    craft new <project_name>
    これにより、指定した名前のディレクトリが作成され、プロジェクトの雛形が展開されます。依存関係のインストールも自動的に行われることがあります ( `craft install` が内部で実行される)。
  3. プロジェクトディレクトリへ移動:
    cd <project_name>
  4. (任意) 依存関係のインストール:
    `craft new` で依存関係がインストールされなかった場合や、後で追加した場合は、手動でインストールします。
    craft install
    または
    pip install -r requirements.txt
  5. 開発サーバーの起動:
    `craft serve` コマンドで開発用サーバーを起動します。
    craft serve
    デフォルトでは `http://localhost:8000` でサーバーが起動し、ブラウザでアクセスするとMasoniteのウェルカムページが表示されます。
    ファイルの変更を検知して自動的にサーバーをリロードさせたい場合は、`–reload` (または `-r`) オプションを使用します。
    craft serve --reload
    または
    craft serve -r
💡 仮想環境の利用推奨: プロジェクトごとに依存関係を管理するために、`venv` などを使って仮想環境を作成し、その中で作業することを強くお勧めします。
python -m venv venv  # 仮想環境を作成 (Windowsでは python -m venv venv)
source venv/bin/activate  # Linux/macOS で有効化
.\venv\Scripts\activate  # Windows (cmd) で有効化
venv\Scripts\Activate.ps1 # Windows (PowerShell) で有効化

基本的な使い方: Hello World! 👋

簡単な例として、「Hello World」を表示するページを作成してみましょう。

1. ルートの定義

Webアプリケーションへのリクエストは、まずルーティング定義に基づいて適切な処理(通常はコントローラーのアクション)に振り分けられます。ルートは `routes/web.py` ファイルで定義します。

`/hello` というURLへのGETリクエストを処理するルートを追加します。対応するコントローラーとして `HelloWorldController` の `show` メソッドを指定します。

# routes/web.py
from masonite.routes import Route

ROUTES = [
    Route.get("/", "WelcomeController@show"),
    # ↓↓↓ この行を追加 ↓↓↓
    Route.get("/hello", "HelloWorldController@show"),
]

ここでは、コントローラークラスとそのメソッド名を文字列で指定しています (`”HelloWorldController@show”`)。Masoniteはこれを解釈し、対応するクラスとメソッドを呼び出します。

2. コントローラーの作成

次に、リクエストを処理する `HelloWorldController` を作成します。`craft` コマンドを使用すると簡単に雛形を作成できます。

craft controller HelloWorld

これにより、`app/http/controllers/HelloWorldController.py` というファイルが生成されます。中身を編集して、`show` メソッドで “Hello World!” という文字列を返すようにします。

# app/http/controllers/HelloWorldController.py
from masonite.controllers import Controller
from masonite.views import View

class HelloWorldController(Controller):

    def show(self, view: View):
        # 文字列を直接返す場合
        # return "Hello World!"

        # ビュー(テンプレート)を使う場合
        # templates/hello.html をレンダリング
        return view.render("hello", {"message": "Hello World from Controller!"})

上記の例では、`View` オブジェクトをメソッドの引数として受け取り(依存性注入)、`view.render()` メソッドを使ってテンプレートファイル (`templates/hello.html`) をレンダリングしています。テンプレートに変数を渡すこともできます。

3. ビュー(テンプレート)の作成

コントローラーで `view.render(“hello”, …)` を使用した場合、対応するテンプレートファイルを作成する必要があります。`templates` ディレクトリ内に `hello.html` を作成します。

craft view hello

`templates/hello.html` ファイルを編集します。Jinja2テンプレートエンジンの構文が使用できます。

<!DOCTYPE html>
<html>
<head>
    <title>Hello Masonite</title>
</head>
<body>
    <h1>{{ message }}</h1>
</body>
</html>

`{{ message }}` の部分に、コントローラーから渡された `message` 変数の値が表示されます。

4. 確認

開発サーバーを起動(または再起動)し、ブラウザで `http://localhost:8000/hello` にアクセスします。

craft serve -r

“Hello World from Controller!” と表示されれば成功です!🎉

プロジェクトのディレクトリ構造 📁

`craft new` で生成されるMasoniteプロジェクトは、規約に基づいた標準的なディレクトリ構造を持っています。主なディレクトリとその役割は以下の通りです。

ディレクトリ名 説明
app/ アプリケーションの主要なロジックが含まれます。コントローラー、モデル、サービスプロバイダ、ミドルウェアなどが配置されます。
app/http/controllers/ リクエストを処理するコントローラークラスを配置します。
app/models/ データベーステーブルに対応するモデルクラスを配置します。
app/providers/ サービスプロバイダを配置します。フレームワークの起動時やリクエスト処理中にサービスを登録・設定します。
app/http/middleware/ HTTPリクエストの前後に処理を挟むミドルウェアクラスを配置します。
routes/ ルーティング定義ファイルを配置します。web.py (Webリクエスト用) や api.py (APIリクエスト用) などがあります。
config/ アプリケーションの設定ファイルを配置します。データベース接続情報 (database.py)、キャッシュ設定 (cache.py)、メール設定 (mail.py) などが含まれます。
databases/ データベース関連のファイルを配置します。
databases/migrations/ データベーススキーマの変更を管理するマイグレーションファイルを配置します。
databases/seeds/ データベースに初期データ(シードデータ)を投入するためのシーダークラスを配置します。
resources/ フロントエンド関連のリソースを配置します。
resources/templates/ HTMLテンプレートファイル (Jinja2) を配置します。
storage/ フレームワークが生成するファイルや、ユーザーがアップロードしたファイルなどを保存する場所です。
storage/framework/ キャッシュされたビューやセッションファイルなどが保存されます。
storage/static/ コンパイル済みのアセット(CSS, JavaScriptなど)が配置される場所です。(注意:これは静的ファイルを提供するルートではありません。静的ファイルは通常 `public/` ディレクトリに配置します)
public/ 公開される静的ファイル(CSS, JavaScript, 画像など)を配置するディレクトリです。Webサーバーはこのディレクトリをドキュメントルートとして設定することが一般的です。
tests/ ユニットテストや機能テストのコードを配置します。
bootstrap/ フレームワークの起動とオートロードに関するファイルが含まれます。
.env 環境変数を定義するファイルです。データベースの認証情報やAPIキーなど、環境ごとに異なる設定を記述します。このファイルはバージョン管理に含めないでください。
craft Masoniteのコマンドラインインターフェース実行ファイルです。

ルーティングの詳細 🧭

ルーティングは、特定のURLへのリクエストをどのコントローラーのアクションで処理するかを定義する仕組みです。Masoniteでは `routes/web.py` (Web用) や `routes/api.py` (API用) に定義します。

基本的なルート定義

`Route` クラスのHTTPメソッドに対応するクラスメソッド(`get`, `post`, `put`, `patch`, `delete`, `options`)を使用して定義します。

# routes/web.py
from masonite.routes import Route

ROUTES = [
    # GETリクエストを /users にマッピング
    Route.get("/users", "UserController@index"),

    # POSTリクエストを /users にマッピング
    Route.post("/users", "UserController@store"),

    # 特定のユーザー表示 (GET /users/1 など)
    Route.get("/users/@user_id", "UserController@show"),

    # ユーザー更新 (PUT /users/1 など)
    Route.put("/users/@user_id", "UserController@update"),

    # ユーザー削除 (DELETE /users/1 など)
    Route.delete("/users/@user_id", "UserController@destroy"),
]

URL中の `@` で始まる部分はルートパラメータを表します。例えば `/users/@user_id` は `/users/1` や `/users/abc` などにマッチし、`user_id` という名前でその値(`1` や `abc`)をコントローラーメソッドで受け取ることができます。

ルートパラメータの型ヒントと制約

ルートパラメータには型ヒントを指定して、受け付ける値の型を制限したり、正規表現で複雑な制約をかけたりできます。

ROUTES = [
    # user_id は整数のみ受け付ける
    Route.get("/users/@user_id:int", "UserController@show"),

    # slug は英数字とハイフンのみ受け付ける
    Route.get("/posts/@slug:regex([a-zA-Z0-9-]+)", "PostController@show"),
]

名前付きルート

ルートに名前を付けておくと、テンプレート内やリダイレクト時にURLを直接記述する代わりに、名前で参照できて便利です。URL構造が変更されても、名前が変わらなければコードの修正箇所が少なくなります。

ROUTES = [
    Route.get("/users/@user_id", "UserController@show").name("users.show"),
    Route.get("/settings/profile", "SettingsController@profile").name("settings.profile"),
]

テンプレート内での利用例:

<!-- user_id が 5 のユーザー詳細ページへのリンク -->
<a href="{{ route('users.show', {'user_id': 5}) }}">ユーザー詳細</a>

<!-- プロフィール設定ページへのリンク -->
<a href="{{ route('settings.profile') }}">プロフィール設定</a>

ルートグループ

複数のルートに共通のプレフィックス(URLの接頭辞)やミドルウェアを適用したい場合、`Route.group()` を使用すると便利です。

ROUTES = [
    Route.group(
        [
            Route.get("/", "AdminDashboardController@show").name("dashboard"),
            Route.get("/users", "AdminUserController@index").name("users.index"),
            Route.post("/users", "AdminUserController@store").name("users.store"),
        ],
        prefix="/admin",  # URLプレフィックス
        middleware=("auth", "admin"), # 適用するミドルウェア
        name="admin.", # ルート名のプレフィックス
    )
]
# 上記は以下のようにアクセス・参照できます
# GET /admin/ => admin.dashboard
# GET /admin/users => admin.users.index
# POST /admin/users => admin.users.store

コントローラーを使わないルート

単純なビューを表示するだけの場合など、コントローラーを経由せずに直接ビューを返すことも可能です。

from masonite.views import View

ROUTES = [
    Route.get("/about", lambda view: view.render("about")),
    # または View クラスを直接指定
    Route.view("/privacy", "privacy"), # templates/privacy.html を表示
]

コントローラー 🕹️

コントローラーは、ユーザーからのリクエストを受け取り、必要なビジネスロジック(モデルとの連携など)を実行し、最終的にレスポンス(通常はビューのレンダリング結果)を返す役割を担います。MVCアーキテクチャの「C」にあたります。

Masoniteでは、コントローラーは `app/http/controllers` ディレクトリに配置されるクラスとして定義します。`masonite.controllers.Controller` を継承するのが一般的ですが、必須ではありません。

コントローラーの作成

`craft` コマンドで簡単に生成できます。

craft controller PostController

これにより `app/http/controllers/PostController.py` が生成されます。

# app/http/controllers/PostController.py
from masonite.controllers import Controller
from masonite.views import View
from masonite.request import Request

class PostController(Controller):

    def index(self, view: View):
        # 投稿一覧表示ロジック
        posts = [...] # データベースから投稿を取得
        return view.render("posts/index", {"posts": posts})

    def show(self, view: View, request: Request):
        # 特定の投稿表示ロジック
        post_id = request.param("post_id") # ルートパラメータからID取得
        post = ... # IDを使ってデータベースから投稿を取得
        if not post:
            # エラー処理など
            pass
        return view.render("posts/show", {"post": post})

    def create(self, view: View):
        # 新規投稿フォーム表示
        return view.render("posts/create")

    def store(self, request: Request):
        # 新規投稿保存ロジック
        title = request.input("title")
        content = request.input("content")
        # バリデーションなど
        # データベースに保存
        # 保存後、一覧ページなどにリダイレクト
        return request.redirect(name="posts.index") # 名前付きルートへリダイレクト

    # update, destroy などのメソッドも同様に定義

依存性注入 (Dependency Injection)

Masoniteの強力な機能の一つが、サービスコンテナによる依存性注入です。コントローラーのメソッドの引数に型ヒントを指定すると、Masoniteが必要なオブジェクトを自動的に生成して渡してくれます。

from masonite.request import Request
from masonite.response import Response
from masonite.views import View
from masonite.authentication import Auth # 認証用クラス
# from app.models.Post import Post # モデルを使う場合

class PostController(Controller):

    # Request, Response, View, Auth オブジェクトが自動的に注入される
    def store(self, request: Request, response: Response, view: View, auth: Auth):
        if not auth.check(): # 認証済みかチェック
            return response.redirect("/login")

        # リクエストからデータを取得
        title = request.input('title')
        content = request.input('content')

        # バリデーション (別途バリデーションクラスなどを使うのが一般的)
        errors = {}
        if not title:
            errors['title'] = 'タイトルは必須です'
        if not content:
            errors['content'] = '本文は必須です'

        if errors:
            # エラーがあれば、入力値とエラーメッセージを添えてフォームに戻す
            request.session.flash('errors', errors)
            request.session.flash('old_input', request.all())
            return response.redirect('/posts/create')

        # モデルを使ってデータベースに保存
        # user = auth.user()
        # Post.create(title=title, content=content, user_id=user.id)

        request.session.flash('success', '投稿が作成されました!')
        return response.redirect(name='posts.index')

    # ルートパラメータも型ヒントで受け取れる
    def show(self, view: View, post_id: int):
        # post_id は整数として渡される
        post = ... # Post.find(post_id) などで取得
        return view.render("posts/show", {"post": post})

これにより、コントローラー内で `Request()` や `View()` のようにインスタンスを直接生成する必要がなくなり、コードがすっきりと、そしてテストしやすくなります。よく使われるクラス(`Request`, `Response`, `View`, `Auth`, `Session`, `Mail`, `Queue` など)はデフォルトでコンテナに登録されています。

リソースコントローラー

ブログ記事やユーザー管理など、CRUD(作成、読み取り、更新、削除)操作一式を持つリソースに対しては、決まったメソッド名(`index`, `show`, `create`, `store`, `edit`, `update`, `destroy`)を持つコントローラーを作成することが一般的です。`craft` コマンドでリソースコントローラーの雛形を生成することもできます。

craft controller ArticleController --resource

対応するルート定義も `Route.resource()` で簡単に設定できます。

# routes/web.py
ROUTES = [
    Route.resource("/articles", "ArticleController"),
    # これにより、以下のルートが自動的に生成される
    # GET /articles => ArticleController@index (一覧) .name("articles.index")
    # GET /articles/create => ArticleController@create (作成フォーム) .name("articles.create")
    # POST /articles => ArticleController@store (保存処理) .name("articles.store")
    # GET /articles/@article => ArticleController@show (詳細表示) .name("articles.show")
    # GET /articles/@article/edit => ArticleController@edit (編集フォーム) .name("articles.edit")
    # PUT/PATCH /articles/@article => ArticleController@update (更新処理) .name("articles.update")
    # DELETE /articles/@article => ArticleController@destroy (削除処理) .name("articles.destroy")
]

ビューとテンプレートエンジン (Jinja2) 🎨

ビューは、ユーザーに表示されるHTMLを生成する役割を担います。Masoniteでは、デフォルトで強力なテンプレートエンジンである Jinja2 を使用します。テンプレートファイルは通常 `resources/templates` ディレクトリに配置されます。

コントローラーからビューへのデータ渡し

コントローラー内で `View` オブジェクトの `render` メソッドを使用し、テンプレート名と渡したいデータを辞書形式で指定します。

# PostController.py
from masonite.views import View
# from app.models.Post import Post

class PostController:
    def show(self, view: View, post_id: int):
        post = { "id": post_id, "title": "Masonite入門", "author": "田中" } # 仮のデータ (実際はモデルから取得)
        comments = [
            {"author": "佐藤", "body": "わかりやすい!"},
            {"author": "鈴木", "body": "参考になります。"},
        ]
        return view.render("posts/show", {"post": post, "comments": comments})

テンプレート (Jinja2) の基本構文

Jinja2テンプレートでは、特別な構文を使って動的なコンテンツを埋め込みます。

  • {{ ... }}: 式の評価結果を出力します。変数や関数の呼び出し結果などを表示します。HTMLエスケープが自動的に行われます。
  • {% ... %}: 制御構文(if文、forループなど)を記述します。
  • {# ... #}: コメントを記述します。出力には含まれません。

例: `resources/templates/posts/show.html`

<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
    {# ここはコメントです #}
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>著者: {{ post.author }}</p>

    <h2>コメント</h2>
    {% if comments %}
        <ul>
            {% for comment in comments %}
                <li>
                    <strong>{{ comment.author }}:</strong>
                    {{ comment.body }}
                </li>
            {% endfor %}
        </ul>
    {% else %}
        <p>コメントはまだありません。</p>
    {% endif %}

    <!-- ルート名を使って他のページへのリンクを生成 -->
    <p><a href="{{ route('posts.index') }}">投稿一覧へ戻る</a></p>
</body>
</html>

テンプレートの継承

多くのWebページで共通のヘッダー、フッター、サイドバーなどのレイアウトを使用する場合、テンプレートの継承機能を使うと便利です。ベースとなるレイアウトテンプレートを定義し、各ページはその一部(ブロック)を上書きします。

例: ベースレイアウト `resources/templates/layouts/app.html`

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Application{% endblock %}</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
    {% block head_extra %}{% endblock %}
</head>
<body>
    <nav class="navbar is-light" role="navigation" aria-label="main navigation">
        <!-- Navbar content -->
        <div class="navbar-brand">
            <a class="navbar-item" href="/">
                MyApp
            </a>
        </div>
    </nav>

    <section class="section">
        <div class="container">
            {% block content %}
                <!-- ここに各ページのコンテンツが入る -->
            {% endblock %}
        </div>
    </section>

    <footer class="footer">
      <div class="content has-text-centered">
        <p>
          © 2025 MyApp.
        </p>
      </div>
    </footer>
    {% block scripts %}{% endblock %}
</body>
</html>

例: 上記レイアウトを継承するページ `resources/templates/home.html`

{% extends 'layouts/app.html' %}

{% block title %}ホーム - {{ super() }}{% endblock %} {# super()で親ブロックの内容を継承 #}

{% block content %}
    <h1 class="title">ようこそ!</h1>
    <p>ここはホームページのコンテンツです。</p>
{% endblock %}

{% block scripts %}
    <!-- このページ固有のJavaScriptなど -->
    <script src="/path/to/home.js"></script>
{% endblock %}

`{% extends ‘…’ %}` でベーステンプレートを指定し、`{% block block_name %}` … `{% endblock %}` で上書きしたい部分を定義します。

ヘルパー関数

Masoniteはテンプレート内で使える便利なヘルパー関数をいくつか提供しています。例えば `route()` 関数は名前付きルートからURLを生成します。他にも `config()`, `session()`, `auth()` などがあります。これらはサービスコンテナ経由でビューに自動的に提供されます。

モデルとMasonite ORM 💾

モデルは、アプリケーションのデータとその操作ロジックを表現します。通常、データベースのテーブルと1対1で対応します。Masoniteでは、Masonite ORM という独自のORM(Object-Relational Mapper)ライブラリを使用します。これはLaravelのEloquent ORMに強く影響を受けており、Active Recordパターンを採用しています。

Masonite ORMは元々Orator ORMの代替として開発されましたが、Masoniteフレームワークから独立して他のPythonプロジェクトでも利用可能です。

ORMを使うことで、SQLを直接書かずにPythonのオブジェクト操作を通じてデータベースとやり取りできます。

モデルの作成

`craft` コマンドでモデルクラスの雛形を作成できます。

craft model User

これにより `app/models/User.py` が生成されます。

# app/models/User.py
from masoniteorm.models import Model

class User(Model):
    """User Model"""
    # __fillable__ や __hidden__ などの属性を定義可能
    # __fillable__ = ['name', 'email', 'password'] # マスアサインメント可能な属性
    # __hidden__ = ['password', 'remember_token'] # JSON変換時に隠す属性
    pass

モデルクラス名は単数形(例: `User`)、対応するテーブル名は複数形(例: `users`)とするのが規約です。もしテーブル名が規約と異なる場合は、`__table__` 属性で明示的に指定できます。

class LegacyUser(Model):
    __table__ = 'old_users_table'

データベース設定

データベースへの接続情報は `config/database.py` で設定します。`.env` ファイルに実際の認証情報などを記述し、`config/database.py` から参照するのが一般的です。

# config/database.py
from masoniteorm.connections import ConnectionResolver
import os

DATABASES = {
    "default": "sqlite", # 使用するデフォルトのコネクション名
    "sqlite": {
        "driver": "sqlite",
        "database": os.path.join(os.path.dirname(os.path.dirname(__file__)), "database.db"),
    },
    "mysql": {
        'driver': 'mysql',
        'host': os.getenv('DB_HOST', '127.0.0.1'),
        'port': os.getenv('DB_PORT', '3306'),
        'database': os.getenv('DB_DATABASE', 'masonite'),
        'user': os.getenv('DB_USERNAME', 'root'),
        'password': os.getenv('DB_PASSWORD', ''),
        'prefix': ''
    },
    # PostgreSQL など他の設定も追加可能
}

DB = ConnectionResolver().set_connection_details(DATABASES)
# .env ファイルの例 (MySQL)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=my_masonite_app
DB_USERNAME=myuser
DB_PASSWORD=mypassword

マイグレーション

マイグレーションは、データベースのスキーマ(テーブル構造など)をバージョン管理するための仕組みです。SQLを直接実行する代わりに、Pythonコードでスキーマの変更を定義し、`craft` コマンドで適用・ロールバックできます。

マイグレーションファイルの作成:

# usersテーブルを作成するマイグレーション
craft migration create_users_table --create users

# postsテーブルに category_id カラムを追加するマイグレーション
craft migration add_category_id_to_posts_table --table posts

これにより `databases/migrations/` ディレクトリにタイムスタンプ付きのファイルが生成されます。中身を編集してスキーマの変更内容を記述します。

# databases/migrations/YYYY_MM_DD_HHMMSS_create_users_table.py
from masoniteorm.migrations import Migration

class CreateUsersTable(Migration):
    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create("users") as table:
            table.increments("id")
            table.string("name")
            table.string("email").unique()
            table.string("password")
            table.timestamps() # created_at と updated_at カラムを追加

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

マイグレーションの実行:

craft migrate

最後のマイグレーションをロールバック:

craft migrate:rollback

全てのマイグレーションをロールバック:

craft migrate:reset

全てのテーブルを削除してマイグレーションを再実行(開発時に便利):

craft migrate:refresh

基本的なCRUD操作

モデルクラスを使って、データの作成、読み取り、更新、削除を行います。

from app.models.User import User

# --- 作成 (Create) ---
# 方法1: インスタンス化してsave()
new_user = User()
new_user.name = "山田太郎"
new_user.email = "yamada@example.com"
new_user.password = "..." # パスワードはハッシュ化が必要
new_user.save()

# 方法2: create()メソッド (マスアサインメント)
# 事前にモデルで __fillable__ の設定が必要
user = User.create({
    "name": "鈴木一郎",
    "email": "suzuki@example.com",
    "password": "..."
})

# --- 読み取り (Read) ---
# 全件取得
all_users = User.all()

# IDで取得 (見つからない場合は None)
user_by_id = User.find(1)

# IDで取得 (見つからない場合は例外発生)
user_by_id_or_fail = User.find_or_fail(1)

# 条件を指定して取得 (最初の1件)
first_user = User.where("name", "山田太郎").first()

# 条件を指定して取得 (複数件)
active_users = User.where("is_active", True).get()

# 条件を複数指定
admin_users = User.where("role", "admin").where("is_active", True).order_by("created_at", "desc").get()

# --- 更新 (Update) ---
# 方法1: findで見つけて属性変更してsave()
user_to_update = User.find(1)
if user_to_update:
    user_to_update.name = "山田花子"
    user_to_update.save()

# 方法2: update()メソッド (条件に合う複数件を更新可能)
# 更新された行数を返す
updated_count = User.where("is_active", False).update({"status": "inactive"})

# --- 削除 (Delete) ---
# 方法1: findで見つけてdelete()
user_to_delete = User.find(2)
if user_to_delete:
    user_to_delete.delete()

# 方法2: destroy()メソッド (ID指定で削除)
# 削除された行数を返す
deleted_count = User.destroy(3) # ID 3 のユーザーを削除
deleted_count = User.destroy([4, 5]) # ID 4, 5 のユーザーを削除

# 方法3: 条件を指定して削除
User.where("created_at", "<", "2023-01-01").delete()

リレーションシップ

Masonite ORMは、モデル間のリレーションシップ(1対1、1対多、多対多など)を簡単に定義し、関連データを効率的に取得する機能を提供します。

例: User (1) 対 Post (多) のリレーション

# app/models/User.py
from masoniteorm.models import Model
from masoniteorm.relationships import has_many

class User(Model):
    @has_many("id", "user_id") # ("自モデルのキー", "相手モデルの外部キー")
    def posts(self):
        from app.models.Post import Post # 循環参照を避けるためメソッド内でインポート
        return Post

# app/models/Post.py
from masoniteorm.models import Model
from masoniteorm.relationships import belongs_to

class Post(Model):
    @belongs_to("user_id", "id") # ("自モデルの外部キー", "相手モデルのキー")
    def user(self):
        from app.models.User import User
        return User

リレーションを使ったデータの取得:

# 特定のユーザーの投稿を取得
user = User.find(1)
user_posts = user.posts().get() # または user.posts

# 特定の投稿の著者を取得
post = Post.find(5)
author = post.user().first() # または post.user

# Eager Loading (N+1問題の回避)
# ユーザー一覧と、各ユーザーの投稿を効率的に取得
users_with_posts = User.with_("posts").get()
for user in users_with_posts:
    print(f"User: {user.name}")
    for post in user.posts: # ここで追加のクエリは発生しない
        print(f"- Post: {post.title}")

他にも様々なリレーションシップ(`has_one`, `belongs_to_many`, `has_many_through` など)がサポートされています。

その他の重要な機能 🧩

サービスコンテナ (IoC Container)

Masoniteの中核をなす機能の一つです。クラスとその依存関係を管理し、必要な場所で自動的にインスタンスを提供(依存性注入)します。これにより、コードのモジュール性が高まり、テストや機能の入れ替えが容易になります。`app/providers` ディレクトリにあるサービスプロバイダを使って、独自のクラスや設定をコンテナに登録できます。

ミドルウェア

HTTPリクエストがコントローラーに到達する前や、レスポンスがクライアントに返される後に、共通の処理を挟むための仕組みです。認証チェック、CSRF保護、リクエストログ記録、CORSヘッダーの追加などに利用されます。ミドルウェアは `app/http/middleware` に作成し、`config/middleware.py` でグローバルに適用するか、ルート定義で個別に指定します。

# config/middleware.py
from app.http.middleware.VerifyCsrfToken import VerifyCsrfToken
from masonite.middleware import SessionMiddleware, ResponseMiddleware, MaintenanceModeMiddleware

HTTP_MIDDLEWARE = [
    MaintenanceModeMiddleware, # メンテナンスモードチェック
    SessionMiddleware,         # セッション管理
    VerifyCsrfToken,           # CSRFトークン検証
    ResponseMiddleware,        # レスポンス処理
    # 他のグローバルミドルウェア
]

ROUTE_MIDDLEWARE = {
    'auth': 'app.http.middleware.AuthenticationMiddleware.AuthenticationMiddleware',
    'guest': 'app.http.middleware.RedirectIfAuthenticatedMiddleware.RedirectIfAuthenticatedMiddleware',
    # 他のルートミドルウェア
}

ルートでの利用例:

Route.get("/dashboard", "DashboardController@show").middleware("auth")

メール送信

SMTP、Mailgun、SESなど、様々なドライバーを通じて簡単にメールを送信できる機能が組み込まれています。設定は `config/mail.py` で行い、`Mail` ファサードや `Mail` クラス(依存性注入で取得)を使って送信します。Mailableクラスを使ってメールの内容や構造をカプセル化することも可能です。

from masonite.facades import Mail

# シンプルなテキストメール
Mail.to("recipient@example.com").send("メール本文")

# Mailableクラスを使う (app/mailables/WelcomeEmail.py などで定義)
# from app.mailables.WelcomeEmail import WelcomeEmail
# Mail.mailable(WelcomeEmail(user)).to(user.email).send()

キューとジョブ

時間のかかるタスク(メール送信、画像処理など)をバックグラウンドで非同期に実行するためのキューシステムを提供します。Redis、Database、SQSなどのドライバーをサポートしています。ジョブクラスを作成し、`Queue` ファサードやクラスを使ってキューに投入します。

# ジョブクラスを作成
craft job ProcessPodcast
# app/jobs/ProcessPodcast.py
from masonite.queues import Queueable

class ProcessPodcast(Queueable):
    def __init__(self, podcast):
        self.podcast = podcast

    def handle(self):
        # 時間のかかるポッドキャスト処理を実行
        print(f"Processing podcast: {self.podcast.title}")
        # ...処理...
        print("Podcast processing complete.")

# キューにジョブを投入
from masonite.facades import Queue
from app.jobs.ProcessPodcast import ProcessPodcast

podcast_data = ...
Queue.push(ProcessPodcast(podcast_data))

# 特定のキューや遅延実行も可能
# Queue.on("emails").later(60).push(SendWelcomeEmail(user)) # 60秒後に emails キューへ

キューを処理するワーカープロセスを起動する必要があります。

craft queue:work

タスクスケジューリング

Cronジョブのように、定期的に実行したいタスク(日次レポート生成、古いデータの削除など)を定義できます。`app/schedule/Kernel.py` にスケジュールを記述し、サーバーで `craft schedule:run` コマンドを毎分実行するように設定します。

# app/schedule/Kernel.py
from masonite.scheduling import Schedule
# from app.tasks.GenerateReport import GenerateReport

class Kernel:
    def __init__(self, schedule: Schedule):
        self.schedule = schedule

    def register(self):
        self.schedule.command("inspire").daily() # craft inspire コマンドを毎日実行
        # self.schedule.call(GenerateReport).daily_at("01:00") # タスククラスを毎日午前1時に実行
        self.schedule.command("cleanup:logs --days=7").weekly() # 週に1回コマンド実行

サーバーのCrontab設定例:

* * * * * cd /path/to/your/project && python craft schedule:run >> /dev/null 2>&1

認証 (Authentication)

ログイン、ログアウト、ユーザー登録、パスワードリセットなど、一般的な認証機能を提供します。`Auth` ファサードやクラスを使って簡単にユーザーの認証状態をチェックしたり、ログイン中のユーザー情報を取得したりできます。設定は `config/auth.py` で行います。

イベントとリスナー

アプリケーション内で特定の出来事(ユーザー登録、注文完了など)が発生した際に、それに応じた処理(ウェルカムメール送信、在庫更新など)を実行するための仕組みです。イベントクラスと、そのイベントを購読するリスナークラスを作成し、`app/providers/EventServiceProvider.py` で関連付けます。

Masoniteを選ぶ理由と他のフレームワークとの比較 🤔

PythonにはDjangoやFlaskなど、成熟した優れたWebフレームワークが存在します。その中でMasoniteがどのような位置づけにあり、どのような場合に選択肢となるかを見てみましょう。

Masonite vs Django

特徴 Masonite Django
思想 開発者中心、モダン、Rails/Laravelライクな規約 Batteries Included、MTV(Model-Template-View)、独自性が強い
学習曲線 比較的緩やか(特にRails/Laravel経験者) やや急(独自の概念が多い)
構造 MVC、サービスコンテナ、サービスプロバイダ MTV、プロジェクト/アプリ構造、settings.py
ORM Masonite ORM (Active Record風) Django ORM (Active Record風だが独自)
柔軟性/拡張性 高い(サービスプロバイダ、サービスコンテナ) 高い(アプリ構造、サードパーティパッケージ豊富)
組み込み機能 豊富(メール、キュー、スケジュール、認証など) 非常に豊富(Adminサイト、フォーム、認証など)
コミュニティ/エコシステム 成長中 巨大で成熟

Masoniteを選ぶシナリオ:

  • Ruby on RailsやLaravelのような開発体験をPythonで求めている場合。
  • モダンな機能(サービスコンテナ、依存性注入など)を積極的に活用したい場合。
  • Djangoの独自規約よりも一般的なWebフレームワークのパターンに慣れている場合。
  • 比較的新しいフレームワークを試してみたい、コミュニティの初期段階から関わりたい場合。

Masonite vs Flask

特徴 Masonite Flask
思想 開発者中心、Batteries Included (選択的) マイクロフレームワーク、シンプル、拡張性は自分で追加
組み込み機能 豊富 最小限(ルーティング、リクエスト/レスポンス、テンプレート程度)
プロジェクト構造 規約ベースで明確 自由度が高い(自分で決める必要がある)
ORM/DB Masonite ORMが付属 自分で選択・導入が必要 (SQLAlchemyなど)
認証/フォームなど 組み込みまたは簡単に導入可能 拡張機能 (Flask-Login, Flask-WTFなど) を使う
学習曲線 最初は覚えることが多いが、規約に沿えば楽 コアは非常にシンプルだが、拡張機能を組み合わせる知識が必要

Masoniteを選ぶシナリオ:

  • フルスタックな機能(ORM、認証、キューなど)を最初から使いたいが、Djangoほど巨大でなくても良い場合。
  • プロジェクトの構造や規約がある程度決まっている方が開発しやすいと感じる場合。
  • Flaskで多くの拡張機能を自分で組み合わせて使うのが手間に感じる場合。

結論として、MasoniteはDjangoのフルスタック性とFlaskのシンプルさ(あるいはモダンさ)の間を狙った、開発者フレンドリーなフレームワークと言えます。特に他のモダンなフレームワークからの移行者や、Pythonで効率的にWebアプリケーションを開発したいと考えている開発者にとって、魅力的な選択肢となるでしょう。

まとめ 🏁

Masoniteは、Pythonエコシステムにおいて比較的新しいながらも、急速に進化しているモダンなWebフレームワークです。開発者の生産性と幸福度を高めることに重点を置き、RailsやLaravelのような他の言語で人気のフレームワークから多くの優れたアイデアを取り入れています。

主な利点:

  • 🚀 開発効率: Craft CLI、サービスコンテナ、規約ベースの構造により、迅速な開発が可能です。
  • 🧩 豊富な機能: ORM、メール、キュー、スケジューリングなど、必要な機能が多く組み込まれています。
  • 🔧 高い拡張性: サービスプロバイダにより、フレームワークのカスタマイズや機能追加が容易です。
  • 📚 学習しやすさ: 他のモダンフレームワーク経験者には馴染みやすい概念が多く、ドキュメントも充実しています。

Djangoの堅牢さとFlaskのシンプルさの良いところ取りを目指し、PythonでのWeb開発に新たな選択肢を提供しています。もしあなたが、開発者体験を重視し、モダンな機能セットを持つフレームワークを探しているなら、Masoniteを試してみる価値は十分にあります。活発なコミュニティと共に、今後ますます魅力的なフレームワークへと成長していくことが期待されます。

ぜひ公式ドキュメント (Masonite Documentation) を参照し、実際に触れてみてください! Happy Crafting! ✨

コメント

タイトルとURLをコピーしました