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
- Ensure you have Python 3.9+ installed.
- Create a virtual environment:
python -m venv venv source venv/bin/activate # On Windows use `venv\Scripts\activate` - Install Masonite via
pip:pip install masonite - Create a new Masonite project:
craft new myapp - 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 theweb.pyfile 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 —
Leave a Reply