Python Masonite Laravel Style Framework

Written by

in

When Python developers crave the elegance of Laravel’s expressive syntax, routing, and powerful ORM, they often turn to Masonite — a modern, Laravel‑style framework built specifically for Python. In this article we’ll explore why Masonite has become the go‑to choice for developers who love Laravel’s conventions, how it mirrors Laravel’s core concepts, and how you can get started building robust web applications with Python Masonite today.

What Is Masonite?

Masonite is a full‑stack web framework written in pure Python. Launched in 2019, it was designed to bring the best practices of Laravel — such as dependency injection, service providers, and expressive routing — to the Python ecosystem. Masonite emphasizes developer happiness, clean code, and rapid prototyping, making it an excellent fit for startups, MVPs, and even large‑scale enterprise projects.

Why Choose a Laravel‑Style Framework in Python?

Laravel has set a high bar for PHP frameworks, offering a cohesive ecosystem that includes:

  • Elegant routing with expressive syntax.
  • A powerful ORM (Eloquent) that simplifies database interactions.
  • Built‑in authentication and authorization scaffolding.
  • Robust testing utilities and a clean service container.

Developers who appreciate these features often wish for a similar experience in Python. Masonite delivers that experience while leveraging Python’s readability and extensive third‑party libraries.

Core Features That Mirror Laravel

1. Expressive Routing

Masonite’s routing system uses a clean, chainable API that feels familiar to Laravel users:

from masonite.routes import Get, Post

ROUTES = [
    Get('/', 'WelcomeController@show').name('home'),
    Post('/login', 'AuthController@login').name('login'),
    Get('/users/@id', 'UserController@show').name('user.show'),
]

Routes can be grouped, named, and middleware can be attached just like in Laravel.

2. Service Container & Dependency Injection

Both frameworks rely on a service container that resolves class dependencies automatically. In Masonite you can bind services in a ServiceProvider:

from masonite.providers import ServiceProvider
from app.services import PaymentGateway

class AppServiceProvider(ServiceProvider):
    def register(self):
        self.app.bind('PaymentGateway', PaymentGateway)

Controllers can then type‑hint the bound class, and Masonite will inject it:

class CheckoutController(Controller):
    def __init__(self, payment_gateway: PaymentGateway):
        self.payment = payment_gateway

    def checkout(self, request):
        self.payment.process(request.all())
        return redirect('/thank-you')

3. ORM – Orm (Inspired by Eloquent)

Masonite ships with an ORM that follows the Active Record pattern, similar to Laravel’s Eloquent. Defining a model is straightforward:

from masoniteorm.models import Model

class Post(Model):
    __table__ = 'posts'
    __fillable__ = ['title', 'content', 'author_id']

Querying records uses chainable methods:

# Fetch published posts with eager‑loaded author
posts = Post.where('status', 'published')\
            .with_('author')\
            .order_by('created_at', 'desc')\
            .get()

4. Blade‑Like Templating with Jinja

While Laravel ships with Blade, Masonite integrates Jinja2 for templating. Jinja’s syntax is similar to Blade, making the transition smooth:

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

{% block content %}
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
{% endblock %}

Custom filters and global functions can be registered in a service provider, just like Blade directives.

5. Authentication & Authorization

Masonite provides a ready‑to‑use authentication system that mirrors Laravel’s Auth scaffolding. Running a single command generates the necessary routes, controllers, and views:

python craft auth:make

For authorization, Masonite offers Gate and Policy classes, allowing you to define granular permissions similar to Laravel’s approach.

Getting Started with Python Masonite

Step‑by‑Step Installation

  1. Ensure you have Python 3.9+ installed.
  2. Create a virtual environment:
    python -m venv venv
    source venv/bin/activate  # On Windows use `venv\Scripts\activate`
    
  3. Install Masonite via pip:
    pip install masonite
    
  4. Create a new Masonite project:
    craft new myapp
    
  5. Run the development server:
    python craft serve
    

Visit http://localhost:8000 and you’ll see the default Masonite welcome page.

Directory Structure Overview

Masonite’s project layout mirrors Laravel’s, which helps developers feel at home:

  • app/ – Contains Controllers, Models, Services, and Providers.
  • routes/ – Holds the web.py file where all HTTP routes are defined.
  • resources/views/ – Stores Jinja templates.
  • database/migrations/ – Migration files for database schema changes.
  • .env – Environment configuration, similar to Laravel’s .env.

Creating a Simple CRUD Module

Below is a quick example of a “Task” CRUD module that demonstrates the Laravel‑style workflow.

1. Generate Model & Migration

python craft model:make Task --migration

2. Define Migration

# database/migrations/2024_10_14_000001_create_tasks_table.py
from masoniteorm.migrations import Migration

class CreateTasksTable(Migration):
    def up(self):
        (
            self.schema.create('tasks')
            .id()
            .string('title')
            .text('description')
            .boolean('completed').default(False)
            .timestamps()
        )

    def down(self):
        self.schema.drop('tasks')

3. Run Migrations

python craft migrate

4. Create Controller

# app/http/controllers/TaskController.py
from masonite.controllers import Controller
from app.Task import Task

class TaskController(Controller):
    def index(self):
        tasks = Task.all()
        return self.view('tasks/index', {'tasks': tasks})

    def create(self):
        return self.view('tasks/create')

    def store(self, request):
        Task.create(request.only(['title', 'description']))
        return self.redirect('/tasks')

    def edit(self, task: Task):
        return self.view('tasks/edit', {'task': task})

    def update(self, request, task: Task):
        task.update(request.only(['title', 'description', 'completed']))
        return self.redirect('/tasks')

    def destroy(self, task: Task):
        task.delete()
        return self.redirect('/tasks')

5. Define Routes

# routes/web.py
from masonite.routes import Get, Post, Put, Delete

ROUTES = [
    Get('/tasks', 'TaskController@index').name('tasks.index'),
    Get('/tasks/create', 'TaskController@create').name('tasks.create'),
    Post('/tasks', 'TaskController@store').name('tasks.store'),
    Get('/tasks/@task/edit', 'TaskController@edit').name('tasks.edit'),
    Put('/tasks/@task', 'TaskController@update').name('tasks.update'),
    Delete('/tasks/@task', 'TaskController@destroy').name('tasks.destroy'),
]

6. Build Views with Jinja

{% extends "layouts/app.html" %}
{% block content %}
    <h1>Task List</h1>
    <a href="{{ route('tasks.create') }}" class="btn btn-primary">Add New Task</a>
    <ul>
    {% for task in tasks %}
        <li>
            {{ task.title }} - {{ 'Done' if task.completed else 'Pending' }}
            <a href="{{ route('tasks.edit', task.id) }}">Edit</a>
            <form method="POST" action="{{ route('tasks.destroy', task.id) }}" style="display:inline;">
                {{ csrf_field() }}
                <button type="submit" class="link">Delete</button>
            </form>
        </li>
    {% endfor %}
    </ul>
{% endblock %}

With just a few commands and a handful of files, you have a fully functional CRUD interface —

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *