Joseph Mancuso
Posted on December 1, 2018
Masonite and Django
I always get the question of "how does Masonite compare to Django?" or "Why would I choose Masonite over Django?" In this article I'll try to address the many aspects of Masonite and how they compare with Django. This article will likely be an ongoing process of section additions.
Disclaimer
Before I start, I am not saying Django is awful. Django is a great framework and it's been going strong in the Python community for a long time. It is clearly here to stay. I personally like pieces of features from other frameworks and enjoy writing in Python so I grabbed tools and tips and architectural concepts from other frameworks and created something that fit my personal use cases perfectly.
Masonite does things differently and i'll be the first to admit that it's slightly opinionated in the way me and our maintainers code and our developer practices.
If you try out Masonite and realize that it's a great framework then I hope you continue using it and contribute to the community. If you do not like Masonite and instead think that Django is much better than more power to you; I hope you keep using Django and create some awesome software, although we'll miss you :'(.
In each section I'll talk more about Masonite than I do Django because I will assume you understand Django already so it's not that I am purposely being biased or explaining only 1 side in more detail than the other. If i mention things like "Settings" I will assume that you understand how Django settings works.
I'll explain the behind the scenes stuff slightly for Masonite but add links to the documentation if you wish to read more into it and learn how it actually works.
Thoughts
Masonite is completely developer centric. We try to get a framework with the developer in mind and how the developer will be interacting with the framework on a day to day basis and strive to tweak that interaction in a way that make sense. We focused an entire release dedicated to polishing the tiny things in the framework.
Masonite is not a framework written for a news corporation (Django) or written as an april fools joke (Flask). Although these two frameworks have strived for a long time and remain the two most dominant Python frameworks, Masonite will be an excellent fit and is a framework written for developers by developers.
Masonite is for those weekend warrior projects that could become something. It's for that next SaaS idea that you want to write but dread writing all the boiler plate involved in getting it to production or uploading to S3 or integrated with Stripe or getting an API setup to get developers to connect to your service or getting recurring tasks setup with cron or all those annoyances that only take a few minutes to setup but you need to setup 10 different packages just to get where you want to be to start developing.
You KNOW you're going to need whitenoise for static files, you KNOW you're going to need to accept subscriptions with Stripe, you KNOW you're going to need some websocket requests in order to make your application more responsive. Why worry about getting those setup when you can just use a framework that has those for you?
Masonite is the awesome starter project for your next idea all packaged up and waiting to handle your application logic.
Architecture
The architecture between Masonite is completely different than that of Django.
Not only does Masonite follow an MVC architecture and Django follows an MTV architecture.
No, these are not the same. If these were the same then they would be called the same thing. Yes, they both accomplish the exact same thing but they do so in different ways.
Django
Django is designed to break a single application into multiple "apps". This is a good architecture for abstracting a lot of logic away for those developers that require it but many developers don't know which parts of their application need to be in separate apps so they start breaking them up until they realize that it simply created more complexity because now I have to know where I put this specific model in order to play nice.
Django does not have a true MVC architecture. Their architecture as they put it themselves in MTV which is Model Template View.
Masonite
Masonite is very different. Masonite is designed to make a single application like Django but it instead breaks up the logic into multiple controllers which are in their own modules.
Unlike many other Python frameworks, Masonite has a true MVC architecture. Masonite has controller classes and methods that can be used to really abstract away a lot of the business logic in a clean and pragmatic way.
Service Container and Service Providers
Django
Django's version of Masonite's Service Container is their INSTALLED_APPS. Some third party apps require adding an app to the INSTALLED_APPS list which loads features into the framwork. Django uses strings in order for these apps to be loaded in which are located and add features in the app. Some third party packages don't require this.
Masonite
Masonite adopted the concept of a Service Container early on. The Service Container is simply a way to add functionality simply to Masonite which is available throughout the application. This is a form of an IOC container into which objects can be put and retrieved into. Masonite takes it a step further and allows objects to be "resolved" by the container. Simply speaking it will determine what your object needs depending on the parameters and retrieve those objects from the container.
Read more about the Service Container here:
These features are added to Masonite in the form of Service Providers (because they provide a service).
Service Providers can add functionality like new drivers, new features, entire packages, new commands and a lot more. For example, adding a service provider for Mail could add support for sending emails etc etc.
These service providers (from 2.0 onwards) are classes which speeds up the time it takes to serve a request because they do not have to be "found" on the backend.
If you are coming from Django, you can think of Service Providers as a form of "adding a thirdparty app to my application." Although apps and service providers perform very differently under the hood, the very high level concept is similiar.
Drivers and Settings
Django
Django uses a few setting files and abstracts them based on where they should run. For example se may have a settings.py and a settings_local.py. This works and is great but I personally never liked adding all of my settings to a single configuration file. It just seemed messy to me but many developers like it which is fine. I would just prefer another abstraction on a feature level. This can also be done in Django by importing all the settings of individual files into the master setting file if you so choose to do it.
Django doesn't necessarily have a concept of "Drivers" the same way Masonite does but adding these are fairly straight forward for Django. They usually require a pip install and a handful of configuration options.
Masonite
Masonite's setting files are broken down into different features of out the box and is something like:
config/
application.py
mail.py
database.py
...
You can change settings and allow third party application to create them for you.
Masonite also has a concept of drivers. Because of Masonite's architecture, things can be turned on, turned off and switched around at will. If you need a Postgres database connection then you switch from driver from "mysql" to "postgres" which will switch the inner workings of the application to use the Postgres connection. Everything will remain exactly the same but use the other connection.
Masonite also has several drivers ready to go out of the box such as an SMTP driver and a Mailgun driver for sending email. Masonite uses drivers for several things such as file uploading. We can simply switch drivers like:
def show(self, Upload):
Upload.driver('disk').store(..) # stores in the file system
Upload.driver('s3').store(..) # stores into Amazon S3
You can also set default drivers in the configuration file so you don't have to explicitly declare them in your code.
Because of the concept of Masonite contracts, we can ensure that all classes of the same type have the same methods so we can switch functionality at the drop of a hat and everything will work just fine.
If you start off a project uploading everything to a disk and then one of your developers say that we should really move to the cloud then that is fine. Either use the drivers supplied with Masonite or create your own driver.
Scaffolding
This section is a little opinionated and may or may not be the accepted option of doing things but it is only an option with Masonite and you can skip this completely.
The concept of "scaffolding" basically means the ability to add boiler plate directly to your code. Making a model or a controller is extremely repetitive.
- create the file
- create the class
- add the first method to it
We do this 100 times in our application. It doesn't take very long at all but its a mild distraction to the task at hand. It only shaves 15-30 seconds off the development time but if you are doing it 100 times then that could be a considerable amount of time shaved off.
Django
Django doesn't have the concept of scaffolding at the class level. Django has a way to scaffold a new app but not the ability to scaffold a new model or view or template.
Masonite takes this a step further
Masonite
Masonite adds the ability to create models, controllers and templates (which Masonite calls views to keep with the MVC architecture).
We have two options:
- find our way to the controller directory
- create the file in the controller directory
- create the DashboardController class in the controller file
- add the first method to it
or we can just run:
$ craft controller Dashboard
Which will create the file, the class, the first method (which you can obviously rename) and put it in the correct directory for us. It's just the little things in the framework that make it feel really awesome. I use these craft commands all the time.
Remember you don't need to use craft commands. These are typically just helper commands that allow you to shave a few seconds to a few minutes off your development time that day.
Batteries Included
Django and Masonite have a very similar concept of batteries included in terms of features and they can honestly be pretty equal in what they have out of the box but Django needs just a little bit of tweaking for each added feature.
In this arena, it's not so much the amount of features that separates Masonite and Django but HOW a Masonite developer interacts with the objects in the application. The features between Masonite and Django are very comparable and I won't waste time explaining every feature of Django and how it each one compares with Masonite but you can read the Masonite docs for any information on features you are looking for.
Any framework can come with features but it's really how we as developers are interacting with those features that really matters. It's the ways and classes we use to interact with these features that really make or break a framework.
Where Masonite really shines is how it's built and how the application and controllers interacts with the IOC container. This alone is what should really drive a lot of developers to Masonite but it's a little foreign to how we normally develop.
Once you start interacting with Masonite you can further understand the architecture behind it.
You can read more in depth about the architecture in the docs starting with the Request Lifecycle
The Little Things
Masonite really focuses on the little things when developing and really keeps a "Separation of Concerns" approach really well. Separation of Concerns says that units of code should be broken up in a way where they only perform 1 job or jobs in similar categories.
Abstracting Class Methods
Take this example in Django:
def upload_file(request):
if request.method == 'POST':
# Some Logic Here To Upload
return HttpResponseRedirect('/success/url/')
return render(request, 'success.html', {'key': 'value'})
This is Django code that shows the form on a GET request and stores the form on a POST request. In Masonite we break this down further into an UploadController
:
class UploadController:
def show(self):
return view('upload', {'key': 'value'})
def store(self):
# Form Logic Here
return view('success')
According to our routes, the url of upload/ will either point to show or store depending on if its a GET or a POST (these method names can be whatever you want them to be)
So the separation of logic is an awesome perk of Masonite.
Input Data
Masonite understands the concept of input data well.
Take this example again from above in Django:
def upload_file(request):
if request.method == 'POST':
firstname = request.POST.get('firstname')
return HttpResponseRedirect('/success/url/')
request.GET.get('firstname')
return render(request, 'success.html', {'key': 'value'})
Notice that we to specify what type of input we need. This was always a strange concept to me and seemed odd.
Masonite knows the request method and what you need:
class UploadController:
def show(self):
firstname = request().input('firstname') # GET
return view('upload', {'key': 'value'})
def store(self):
firstname = request().input('firstname') # POST
return view('success')
Notice the same code here works whether it is a POST request or a GET request.
Posted on December 1, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.