Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Masonite ships with a really powerful routing engine. Routing helps link a browser URL to its controller and controller action.
Routes are created by importing the Route
class and defining the HTTP verb you would like with the URL and the controller you would like to use. These routes need to be wrapped in a ROUTES
list inside your routes file.
The first parameter is the URL you would like to be available in your application. In the above example, this will allow anybody to go to the /welcome
URL.
The second parameter is the Controller you want to bind this route to.
You may choose to define any one of the available verbs:
In addition to these route verbs you can use built in routes:
There are multiple ways to bind a controller to a route.
You can use a string binding defining the controller class and its method {ControllerClass}@{controller_method}
:
When using string binding, you must ensure that this controller class can be imported correctly and that the controller class is in a registered controller location.
Note that this is the prefered way as it will avoid circular dependencies as no import is required in your route file.
You can import your controllers in your route file and provide a class name or a method class:
Here as no method has been defined the __call__
method of the class will be bound to this route. It means that you should define this method in your controller:
For convenience, you can provide the method class instead:
You can also bind the route to a controller instance:
Here as no method has been defined the __call__
method of the class will be bound to this route. It means that you should define this method in your controller:
You may define several available methods on your routes to modify their behavior during the request.
You can add one or multiple Routes Middlewares:
This will attach the middleware key(s) to the route which will be picked up from your middleware configuration later in the request.
You can exclude one or multiple Routes Middlewares for a specific route:
You can specify a name for your route. This is used to easily compile route information in other parts of your application by using the route name which is much more static than a URL.
You can specify the parameters in the URL that will later be able to be retrieved in other parts of your application. You can do this easily by specify the parameter name attached to a @
symbol:
Sometimes you want to optionally match routes and route parameters. For example you may want to match /dashboard/user
and /dashboard/user/settings
to the same controller method. In this event you can use optional parameters which are simply replacing the @
symbol with a ?
:
You can specify the subdomain you want this route to be matched to. If you only want this route to be matched on a "docs" subdomain (docs.example.com):
Route compilers are a way to match on a certain route parameter by a specific type. For example, if you only watch to match where the @user_id
is an integer. You can do this by appending a :
character and compiler name to the parameter:
Available route compilers are:
integer
int
(alias for integer)
string
signed
uuid
You can also create your own route compilers if you want to be able to support specific regex matches for routes.
All route compilers will need to be added to the top of your register_routes()
method in your Kernel.py
file.
Note: The compile methods need to happen before the routes are loaded in this method so make sure it is at the top. You may also put it in any method that appears before the
register_routes()
method.
Route groups are a great way to group mulitple routes together that have similiar options like a prefix, or multiple routes with the same middleware.
A route uses the group()
method that accepts a list of routes and keyword arguments for the options:
The prefix and name options will prefix the options set in the routes inside the group. In the above example, the names of the routes would dashboard.settings
with a URL of /dashboard/settings
and dashboard.monitor
and a URL of /dashboard/monitor
.
Route views are a quick way to return a view quickly without needing to build a controller just to return a view:
You could optionally pass in the methods you want this to be able to support if you needed to:
Application routes can be listed with the routes:list
Masonite command. Routes will be displayed in a table with relevant info such as route name, methods, controller and enabled middlewares for this route.
Routes can be filtered by methods:
Routes can be filtered by name:
You are browsing the development
version of the documentation for an upcoming version of Masonite. It is subject to change.
Stop using old frameworks with just a few confusing features. Masonite is the developer focused dev tool with all the features you need for the rapid development you deserve. Masonite is perfect for beginners getting their first web app deployed or advanced developers and businesses that need to reach for the full fleet of features available.
Masonite works hard to be fast and easy from install to deployment so developers can go from concept to creation in as quick and efficiently as possible. Use it for your next SaaS! Try it once and you’ll fall in love.
If you are more of a visual learner you can watch Masonite related tutorial videos at MasoniteCasts.com
Mail support for sending emails quickly.
Queue support to speed your application up by sending jobs to run on a queue or asynchronously.
Notifications for sending notifications to your users simply and effectively.
Task scheduling to run your jobs on a schedule (like everyday at midnight) so you can set and forget your tasks.
Events you can listen for to execute listeners that perform your tasks when certain events happen in your app.
A BEAUTIFUL Active Record style ORM called Masonite ORM. Amazingness at your fingertips.
Many more features you need which you can find in the docs!
These, among many other features, are all shipped out of the box and ready to go. Use what you need when you need it.
In order to use Masonite, you’ll need:
Python 3.7+
Latest version of OpenSSL
Pip3
All commands of python and pip in this documentation is assuming they are pointing to the correct Python 3 versions. For example, anywhere you see the python
command ran it is assuming that is a Python 3.7+ Python installation. If you are having issues with any installation steps just be sure the commands are for Python 3.7+ and not 2.7 or below.
If you are running on a Linux flavor, you’ll need the Python dev package and the libssl package. You can download these packages by running:
Debian and Ubuntu based Linux distributions
Or you may need to specify your python3.x-dev
version:
Enterprise Linux based distributions (Fedora, CentOS, RHEL, ...)
Be sure to join the Discord Community for help or guidance.
Masonite excels at being simple to install and get going. If you are coming from previous versions of Masonite, the order of some of the installation steps have changed a bit.
Firstly, open a terminal and head to a directory you want to create your application in. You might want to create it in a programming directory for example:
If you are on windows you can just create a directory and open the directory in the Powershell.
Although this step is technically optional, it is highly recommended. You can create a virtual environment if you don't want to install all of masonite's dependencies on your systems Python. If you use virtual environments then create your virtual environment by running:
or if you are on Windows:
The python
command here is utilizing Python 3. Your machine may run Python 2 (typically 2.7) by default for UNIX machines. You may set an alias on your machine for Python 3 or simply run python3
anytime you see the python
command.
For example, you would run python3 -m venv venv
instead of python -m venv venv
First install the Masonite package:
Then start a new project:
This will create a new project in the current directory.
If you want to create the project in a new directory (e.g. my_project
) you must provide the directory name with project start my_project
.
Then install Masonite dependencies:
If you have created the project in a new directory you must go to this directory before running project install
.
Once installed you can run the development server:
Congratulations! You’ve setup your first Masonite project! Keep going to learn more about how to use Masonite to build your applications.
Controllers are a place where most of your business logic will be. Controllers are where you put the responses you see in the web browser. Responses can be dictionaries, lists, views or any class that can render a response.
You may use a craft command to create a new basic controller or simply make a new controller manually. Controllers are classes with methods that are mapped to a route.
Your route may look something like this:
In this case this route will call the WelcomeController
classes show
method.
To create a basic controller via a craft command simply run:
This will create a new controller class to get you setup quickly. This controller class will look like a basic class like this:
You may start building your controller out and adding the responses you need.
Note that the controllers inherit Masonite base
Controller
class. This is required for Masonite to pick up your controller class in routes.
Your controller's constructor and controller methods are resolved by Masonite's Service Container. Because of this, you can simply typehint most of Masonite's classes in either the constructor or the methods:
Read more about the benefits of the Service Container.
Controllers can have different response types based on what you need to return.
If you want to return a JSON response you can return a dictionary or a list:
This will return an application/json
response.
You can return strings:
If you want to return a view you can resolve the view class and use the render method:
If you are using Masonite ORM, you can return a model directly:
If you want to return a redirect you can resolve the response class and use the redirect method:
You can return any class that contains a get_response()
method. This method needs to return any one of the above response types.
If you had a parameter in your route, you may fetch it by specifying the parameter in the controller's method:
Since the id
parameter is in the route we can fetch the value of this parameter by specifying it in the controller method signature:
Another way to fetch route parameters is through the request class:
Masonite will be able to pick up controllers (inheriting Controller
class) using string binding in the registered controllers locations.
The default registered controllers location is app/controllers
and is defined in project Kernel.py
configuration file:
You can override the registered controllers location in Kernel.py
file by editing the default binding controllers.location
.
You can multiple additional controller locations with add_controller_locations
:
The best place to do this is in your Kernel.py
file in the register_routes()
method.
You should do it before registering routes, else registering routes will fail as Masonite will fail to resolve controller classes.
This section of the documentation will contain various tutorials. These are guides that are designed to take you from beginning to end on building various types of projects with Masonite. We may not explain things in much detail for each section as this part of the documentation is designed to just get you familiar with the inner workings of Masonite.
Since this section of the documentation is designed to just get you up and coding with Masonite, any further explanations that should be presented are inside various "hint blocks." Once you are done with the tutorial or simply want to learn more about a topic it is advised that you go through each hint block and follow the links to dive deeper into the reference documentation which does significantly more explaining.
You will see various hint blocks throughout the tutorials. Below are examples of what the various colors represent.
You'll see hint blocks that are green which you should follow if you want to learn more information about the topic currently being discussed.
You'll also see hint blocks that are blue. These should not be ignored and typically contain background information you need to further understand something.
This tutorial will assume you have already installed Masonite. If you haven't, be sure to read the Installation guide to get a fresh install of Masonite up and running. Once you have one up and running or if you already have it running, go ahead and continue on.
In this tutorial we will walk through how to create a blog. We will touch on all the major systems of Masonite and it should give you the confidence to try the more advanced tutorials or build an application yourself.
Typically your first starting point for your Masonite development flow will be to create a route. All routes are located in routes/web.py
and are extremely simple to understand. They consist of a request method and a route method. Routing is simply stating what incoming URI's should direct to which controllers.
For example, to create a GET
request route it will look like:
We'll talk more about the controller in a little bit.
You can read more about routes in the Routing documentation
We will start off by creating a view and controller to create a blog post.
A controller is a simply a class that inherits from Masonite's Controller class and contains controller methods. These controller methods will be what our routes will call so they will contain most of our application's business logic.
Think of a controller method as a function in the views.py
file if you are coming from the Django framework
Let's create our first route now. We can put all routes inside routes/web.py
and inside the ROUTES
list. You'll see we have a route for the home page. Let's add a route for creating blogs.
You'll notice here we have a BlogController@show
string. This means "use the blog controller's show method to render this route". The only problem here is that we don't yet have a blog controller.
All controllers are located in the app/controllers
directory by default and Masonite promotes a 1 controller per file structure. This has proven efficient for larger application development because most developers use text editors with advanced search features such as Sublime, VSCode or Atom. Switching between classes in this instance is simple and promotes faster development. It's easy to remember where the controller exactly is because the name of the file is the controller.
You can of course move controllers around wherever you like them but the craft command line tool will default to putting them in separate files. If this seems weird to you it might be worth a try to see if you like this opinionated layout.
Like most parts of Masonite, you can scaffold a controller with a craft command:
This will create a controller in app/controllers
directory that looks like this:
Simple enough, right? You'll notice we have a show
method we were looking for. These are called "controller methods" and are similiar to what Django calls a "view."
But also notice we now have our show method that we specified in our route earlier.
We can return a lot of different things in our controller but for now we can return a view from our controller. A view in Masonite are html files or "templates". They are not Python objects themselves like other Python frameworks. Views are what the users will see (or view).
This is important as this is our first introduction to Masonite's IOC container. We specify in our parameter list that we need a view class and Masonite will inject it for us.
For now on we won't focus on the whole controller but just the sections we are worried about. A ...
means there is code in between that we are not worried about:
Notice here we "type hinted" the View
class. This is what Masonite calls "Auto resolving dependency injection". If this doesn't make sense to you right now don't worry. The more you read on the more you will understand.
You'll notice now that we are returning the blog
view but it does not exist yet.
All views are in the templates
directory. We can create a new file called templates/blog.html
.
We can put some text in this file like:
Let's run the migration for the first time:
and then run the server
and open up http://localhost:8000/blog
. You will see "This is a blog" in your web browser.
Most applications will require some form of authentication. Masonite comes with a craft command to scaffold out an authentication system for you. This should typically be ran on fresh installations of Masonite since it will create controllers, routes, and views for you.
For our blog, we will need to setup some form of registration so we can get new users to start posting to our blog. We can create an authentication system by running the craft command:
We should get a success message saying that some new assets were created. You can check your controllers folder and you should see a few new controllers there that should handle registrations.
You can then add the authentication routes to your project:
We will check what was created for us in a bit.
In order to register these users, we will need a database. By default, Masonite uses SQLite. If you want to use a different database you can change the options that start with DB_
in your .env
file. For running MySQL or Postgres you will need to have those databases setup already.
We have already run the migration command before, which was:
If you want to use MySQL, open up the .env
file in the root of your project and change the DB_DATABASE
to mysql
. Also, feel free to change the DB_DATABASE
name to something else.
Once you have set the correct credentials, we can go ahead and migrate the database. Out of the box, Masonite has a migration for a users table which will be the foundation of our user. You can edit this user migration before migrating but the default configuration will suit most needs just fine and you can always add or remove columns at a later date.
This will create our users table for us along with a migrations table to keep track of any migrations we add later.
Now that we have the authentication and the migrations all migrated in, let's create our first user. Remember that we ran craft auth
so we have a few new templates and controllers.
Go ahead and run the server:
and head over to http://localhost:8000/register and fill out the form. You can use whatever name and email you like but for this purpose we will use:
We have looked at running migrations but let's look at how to create a new migration.
Now that we have our authentication setup and we are comfortable with migrating our application, let's create a new migration where we will store our posts.
Our posts table should have a few obvious columns that we will simplify for this tutorial part.
Not surprisingly, we have a command to create migrations. You can read more about Database Migrations here but we'll simplify it down to the command and explain a little bit of what's going on:
This command simply creates the start of a migration file that we will use to create the posts table. By convention, table names should be plural (and model names should be singular but more on this later).
This will create a migration in the databases/migrations
folder. Let's open that up and starting on line 6 we should see something that looks like:
Lets add a title, an author, and a body to our posts tables.
This should be fairly straight forward but if you want to learn more, be sure to read the Database Migrations documentation.
Now we can migrate this migration to create the posts table
Now that we have our tables and migrations all done and we have a posts table, let's create a model for it.
Models in Masonite are a bit different than other Python frameworks. Masonite uses an Active Record ORM. Models and migrations are separate in Masonite. Our models will take shape of our tables regardless of what the table looks like.
Again, we can use a craft command to create our model:
Notice we used the singular form for our model. By default, Masonite ORM will check for the plural name of the class in our database (in this case posts) by assuming the name of the table is the plural word of the model name. We will talk about how to specify the table explicitly in a bit.
The model created now resides inside app/models/Post.py
and when we open it up it should look like:
Simple enough, right? Like previously stated, we don't have to manipulate the model. The model will take shape of the table as we create or change migrations.
Again, the table name that the model is attached to is the plural version of the model name but if you called your table something different such as "user_posts" instead of "posts" we can specify the table name explicitly:
Masonite ORM by default protects against mass assignment as a security measure so we will explicitly need to set what columns we would like to be fillable (this way we can pass the column names into the create
and update
methods later).
The relationship is pretty straight forward here. Remember that we created a foreign key in our migration. We can create that relationship in our model like so:
Because of how Masonite does models, some models may rely on each other so it is typically better to perform the import inside the relationship like we did above to prevent any possibilities of circular imports.
We won't go into much more detail here about different types of relationships but to learn more, refer to Masonite ORM Relationships documentation.
Let's setup a little HTML so we can learn a bit more about how views work. In this part we will setup a really basic template in order to not clog up this part with too much HTML but we will learn the basics enough that you can move forward and create a really awesome blog template (or collect one from the internet).
Now that we have all the models and migrations setup, we have everything in the backend that we need to create a layout and start creating and updating blog posts.
We will also check if the user is logged in before creating a template.
The URL for creating will be located at /blog/create
and will be a simple form for creating a blog post
Notice here we have a {{ csrf_field }}
below the <form>
open tag. Masonite comes with CSRF protection so we need a token to render the hidden field with the CSRF token.
Now we need to make sure the user is logged in before creating this so let's change up our template a bit:
auth()
is a view helper function that either returns the current user or returns None
.
Masonite uses Jinja2 templating so if you don't understand this templating, be sure to Read Their Documentation.
For simplicity sake, we won't be styling our blog with something like Bootstrap but it is important to learn how static files such as CSS works with Masonite so let's walk through how to add a CSS file and add it to our blog.
Firstly, head to storage/static/
and make a blog.css
file and throw anything you like in it. For this tutorial we will make the html page slightly grey.
Now we can add it to our template like so right at the top:
That's it. Static files are really simple. It's important to know how they work but for this tutorial we will ignore them for now and focus on more of the backend.
Javascript files are the same exact thing:
For more information on static files, checkout the Static Files documentaton.
Notice that our action is going to /blog/create
so we need to direct a route to our controller method. In this case we will direct it to a store
method.
Let's open back up the routes/web.py
file and create a new route. Just add this to the ROUTES
list:
and create a new store method on our controller:
Now notice above in the form we are going to be receiving 2 form inputs: title and body. So let's import the Post
model and create a new post with the input.
Notice that we now used request: Request
here. This is the Request
object. Where did this come from? This is the power and beauty of Masonite and your first introduction to the Service Container. The Service Container is an extremely powerful implementation as allows you to ask Masonite for an object (in this case Request
) and get that object. This is an important concept to grasp so be sure to read the documentation further.
Also notice we used an input()
method. Masonite does not discriminate against different request methods so getting input on a GET
or a POST
request are done exactly the same way by using this input
method.
Go ahead and run the server using craft serve again and head over to http://localhost:8000/blog
and create a post. This should hit the /blog/create
route with the POST
request method and we should see "post created".
Lets go ahead and show how we can show the posts we just created. Now that we are more comfortabale using the framework, in this part we will create 2 new templates to show all posts and an individual post.
Let's create 2 new templates.
templates/posts.html
templates/single.html
Let's start with showing all posts
Let's create a controller for the posts to separate it out from the BlogController
.
Great! So now in our show
method we will show all posts and then we will create a single
method to show a specific post.
Let's get the show
method to return the posts view with all the posts:
We need to add a route for this method:
Our posts view can be very simple:
Go ahead and run the server and head over to http://localhost:8000/posts
route. You should see a basic representation of your posts. If you only see 1, go to http://localhost:8000/blog
to create more so we can show an individual post.
Showing The Author
Remember we made our author relationship before. Masonite ORM will take that relationship and make an attribute from it so we can display the author's name as well:
Let's repeat the process but change our workflow a bit.
Next we want to just show a single post. We need to add a route for this method:
Notice here we have a @id
string. We can use this to grab that section of the URL in our controller in the next section below. This is like a route URL capture group.
Let's create a single
method so we show a single post.
We use the param()
method to fetch the id from the URL. Remember this key was set in the route above when we specified the @id
For a real application we might do something like @slug
and then fetch it with request().param('slug')
.
We just need to display 1 post so lets just put together a simple view:
Go ahead and run the server and head over the http://localhost:8000/post/1
route and then http://localhost:8000/post/2
and see how the posts are different.
By now, all of the logic we have gone over so far will take you a long way so let's just finish up quickly with updating and deleting posts. We'll assume you are comfortable with what we have learned so far so we will run through this faster since this is just more of what were doing in the previous parts.
Let's just make an update method on the PostController
:
Since we are more comfortable with controllers we can go ahead and make two at once. We made one that shows a view that shows a form to update a post and then one that actually updates the post with the database.
templates/update.html
Remember we made 2 controller methods so let's attach them to a route here:
That should be it! We can now update our posts.
Let's expand a bit and make a delete method.
Notice we used a GET
route here, It would be much better to use a POST
method but for simplicity sake will assume you can create one by now. We will just add a link to our update method which will delete the post.
We can throw a delete link right inside our update template:
Great! You now have a blog that you can use to create, view, update and delete posts! Go on to create amazing things!
Masonite uses SEMVER versioning schema and your requirements should be fixed on any one of these requirements depending on your dependency manager:
This will allow your application to stay up to date for all minor releases of the framework.
Masonite currently has an 8 month release cycle. Roughly 8 months we will come out with a new major release. Masonite follows a SEMVER versioning schema
Major releases almost always contain some form of breaking changes. You should only upgrade to major versions after careful local upgrades and testing.
Minor versions come with new features and could release every few days or every few months depending on how many features are being added to the codebase and when those features are ready to be released.
Minor version updates will never come with breaking changes. You should always stay up to date with the latest minor versions of the framework.
Patch versions are small fixes or security releases.
Patch version updates will never come with breaking changes. You should always stay up to date with the latest patch versions of the framework.
There are plenty of ways to contribute to open source. Many of which don't even rely on writing code. A great open source project should have excellent documentation, a thriving community and have as little bugs as possible. Below I will explain how to contribute to this project in different ways both including and exluding code contributions.
This is not an exhaustive list and not the only ways to contribute but they are the most common. If you know of other ways to contribute then please let us know.
Of course the project requires contributions to the main development aspects but it's not the only way. But if you would like to contribute to development then a great way to get started is to simply read through this documentation. Get acquainted with how the framework works, how Controllers and Routing work and read the Architectural Concepts documentation starting with the Request Lifecycle, then the Service Providers and finally the Service Container.
It would also be good to read about the Release Cycle to get familiar with how Masonite does releases (SemVer and RomVer).
Feature Maintainers are people who are in charge of specific features (such as Caching or Creating Packages). These developers will be in charge of reviewing PR's and merging them into the development branch and also have direct contact with the repository owner to discuss.
Feature maintainers must already have significant contributions to development of the repository they are trying to be a Feature Maintainer for. Although they do not have to be contributors to the actual feature they plan to maintain.
If you don't want to touch the code and instead want to just look at it and figure it out, contribute some comments! Comments are an excellent way for future developers to read and understand the framework. Masonite strives on being extremely commented. Although most of the code itself does not need to be commented, some of the classes, modules, methods and functions do (although a lot of them already are).
Comments don't affect the working code so if you want to get used to contributing to open source or you just don't quite understand what a class method is doing or you are afraid of contributing and breaking the project (there are tests) then contributing comments is right for you!
Masonite package requires testing. If you want to search through all the tests in the tests directories of those repositories and write additional tests and use cases then that will be great! There are already over 100 tests but you can always write more. With more testing comes more stability. Especially as people start to contribute to the project. Check the tests that are already there and write any use cases that are missing. These tests can be things such as special characters in a url or other oddities that may not have been thought of when using TDD for that feature.
Once familiar with the project (by either contributing or by building application using the framework) it would be excellent if you could write or record tutorials and put them on Medium or YouTube. In order for the framework to be successful, it needs to have a plethora of documentation even outside of this documentation. It needs to have notoriety and if people are seeing the framework pop up in their favorite locations they will be more inclined to use the framework and contribute to it as well.
Plus there will be fantastic tutorials out there for beginners to find and watch and you could also build a following off the back of Masonite.
This documentation is fantastic but there are spots where it could be improved. Maybe we haven't explained something fully or something just doesn't make sense to you. Masonite uses Gitbook.com to host it's documentation and with that you are able to comment directly on the documentation which will start a discussion between you and the documentation collaborators. So if you want to cycle through the documentation page by page and get acquainted with the framework but at the same time contribute to the documentation, this is perfect for you.
If you just don't want to contribute code to the main project you may instead simply report bugs or improvements. You can go ahead and build any of your applications as usual and report any bugs you encounter to the GitHub.com issues page.
Look at the issues page on GitHub.com for any issues, bugs or enhancements that you are willing to fix. If you don't know how to work on them, just comment on the issue and Joseph Mancuso or other core contributors will be more than happy explaining step by step on how you can go about fixing or developing that issue.
If you have a large following on any social media or no following at all, you can contribute by trying to build up a following around Masonite. Any open source project requires an amazing community around the framework. You can either build up a community personally and be the leader of that community or you can simply send them to Masonite's GitHub repository where we can build up a community around there.
Another idea is to use Masonite to build applications such as a screencast website like LaraCasts.com or an official Masonite website or even a social network around Masonite. Every great framework needs it's "ecosystem" so you may be apart of that by building these applications with the Masonite branding and logos. Although copying the branding requires an OK from Joseph Mancuso, as long as the website was built with Masonite and looks clean it shouldn't be a problem at all.
Questions will come in eventually either through the GitHub issues or through websites like StackOverflow. You could make it a priority to be the first to answer these peoples questions or if you don't know the answer you can redirect one of the core maintainers or contributors to the question so we can answer it further.
Most pull requests will sit inside GitHub for a few days while it gets quality tested. The main develop
branch pull requests could sit there for as long as 6 months and will only be merged in on releases. With that being said, you can look at the file changes of these pull requests and ensure they meet the community guidelines, the API is similar to other aspects of the project and that they are being respectful and following pull requests rules in accordance with the Contributing Guide documentation.
Every now and then will be a requires discussion
label on an issue or pull request. If you see this label then be sure to add your thoughts on an issue. All issues are open for discussion and Masonite strives off of developer input so feel free to enter a discussion.
Every framework needs great packages and we as the maintainers of Masonite can only do so much with coming out with great packages and maintaining the framework at the same time. We look forward to our community coming out with awesome additions to the Masonite ecosystem. If you have any issues then be sure to open in the gitter chatroom on the Github homepage.
When contributing to Masonite, please first discuss the change you wish to make via a GitHub issue. Starting with a GitHub issue allows all contributors and maintainers to have a chance to give input on the issue. It creates a public forum to discuss the best way to implement the issue, fix, or improvement. It also creates an open discussion on if the issue will even be permitted to be in the project.
Please note we have a code of conduct, please follow it in all your interactions with the project. You can find it in this in this documentation as well as all of Masonite's repositories.
The framework has 2 main parts: The "masonite" repo and the "cookie-cutter" repo.
The MasoniteFramework/cookie-cutter
repository is the main repository that will install when creating new projects using the project start
command. This is actually a full Masonite project. When you run project start
it simply reaches out to this GitHub repo, fetches the zip and unzips it on your computer. Not much development will be done in this repository and won't be changed unless something requires changes in the default installation project structure.
The MasoniteFramework/core
repository is deprecated and development has been moved into MasoniteFramework/masonite
. This repository contains all the development of Masonite and contains all the releases for Masonite. If you need to fix a bug or add a new feature then this is the repository to fork and make your changes from.
The MasoniteFramework/craft
is deprecated. This was where the craft CLI tool lived that has since been moved into the masonite
repository.
You can read about how the framework flows, works and architectural concepts here
This repo is simple and will be able to be installed following the installation instruction in the README.
Fork the MasoniteFramework/cookie-cutter
repo.
Clone that repo into your computer:
git clone http://github.com/your-username/cookie-cutter.git
Checkout the current release branch (example: develop
)
git checkout -b develop
You should now be on a develop
local branch.
Run git pull origin develop
to get the current release version.
From there simply create your feature branches (<feature|fix>-<issue-number>
) and make your desired changes.
Push to your origin repository:
git push origin change-default-orm
Open a pull request and follow the PR process below
The trick to this is that we need it to be pip installed and then quickly editable until we like it, and then pushed back to the repo for a PR. Do this only if you want to make changes to the core Masonite package
To do this just:
Fork the MasoniteFramework/masonite
repo,
Clone that repo into your computer:
git clone http://github.com/your-username/masonite.git
Activate your masonite virtual environment (optional)
Go to where you installed masonite and activate the environment
While inside the virtual environment, cd
into the directory you installed core.
Run pip install -e .
from inside the masonite directory. This will install masonite as a pip package in editable mode. Any changes you make to the codebase will immediately be available in your project. You may need to restart your development server.
Any changes you make to this package just push to your feature branch on your fork and follow the PR process below.
Comments are a vital part of any repository and should be used where needed. It is important not to overcomment something. If you find you need to constantly add comments, you're code may be too complex. Code should be self documenting (with clearly defined variable and method names)
There are 3 main type of comments you should use when developing for Masonite:
All modules should have a docstring at the top of every module file and should look something like:
Notice there are no spaces before and after the sentence.
All methods and functions should also contain a docstring with a brief description of what the module does
For example:
Most methods will require some dependency or parameters. You must specify them like this:
And if your dependencies are objects it should give the path to the module:
Code comments should be left to a MINIMUM. If your code is complex to the point that it requires a comment then you should consider refactoring your code instead of adding a comment. If you're code MUST be complex enough that future developers may not understand it, add a #
comment above it
For normal code this will look something like:
Please read this process carefully to prevent pull requests from being rejected.
You should open an issue before making any pull requests. Not all features will be added to the framework and some may be better off as a third party package or not be added at all. It wouldn't be good if you worked on a feature for several days and the pull request gets rejected for reasons that could have been discussed in an issue for several minutes.
Ensure any changes are well commented and any configuration files that are added have docstring comments on the variables it's setting. See the comments section above.
Update the MasoniteFramework/docs
repo (and the README.md inside MasoniteFramework/masonite
repo if applicable) with details of changes to the UI. This includes new environment variables, new file locations, container parameters, new feature explanations etc.
Name your branches in the form of <feature|fix>/<issue-number>
. For example if you are doing a bug fix and the issue number is 576
then name your branch fix/576
. This will help us locate the branches on our computers at later dates. If it is a new feature name it feature/576
.
You must add unit testing for any changes made before the PR will be merged. If you are unsure how to write unit tests of the repositories listed above, you may open the pull request anyway and we will add the tests together.
Increase the version numbers in any example files (like setup.py) and to the new version that this Pull Request would represent. The versioning scheme we use is SEMVER.
The PR must pass the GitHub actions that run on pull requests. The Pull Request can be merged in once you have a successful review from two other collaborators, or one review from a maintainer.
Branching is also important. Depending on what fixes or features you are making you will need to branch from (and back into) the current branch. Branching for Masonite simple:
1) All of Masonite repositories, packages, etc. follow the same basic branching flow.
2) Each repository has: a current release branch, previous release branches, a master branch and a develop branch.
3) The current release branch is what the current release is on. So for example, Masonite is on version 2.3 so the current release branch will be 2.3
.
4) Previous release branches are the same thing but instead are just previous versions. So if Masonite is currently on 4.0
then the previous release branches could be 3.0
, 2.1
, 2.2
, etc.
5) The master branch is a staging branch that will eventually be merged into the current release branch. Here is where all non breaking changes will be staged (new non breaking features and bug fixes).
6) The develop
branch is a staging branch to the next major version. So for example, Masonite will be on version 4.0
. If you have an idea for a feature but it will break the existing feature then you will branch from (and back into) the develop
branch. This branch will eventually be merged into 4.1
branch and become apart of the next major version when that is released.
For example, if you want to make a new feature and you know it will not break anything (like adding the ability to queue something) then you will branch from the master branch following the Pull Request flow from above. The PR will be open to the master branch. This feature will then be merged into the current release branch and be released as a new minor version bump (4.1.0
).
Bug fixes that do not break anything is the same process as above.
New features will be the same process but be branched off of the develop
branch. You will make your change and then open a pull request to the develop
branch. This is a long running branch and will be merged once the next major version of Masonite is ready to be released.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment include:
Using welcoming and inclusive language
Being respectful of differing viewpoints and experiences
Gracefully accepting constructive criticism
Focusing on what is best for the community
Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
The use of sexualized language or imagery and unwelcome sexual attention or advances
Trolling, insulting/derogatory comments, and personal or political attacks
Public or private harassment
Publishing others' private information, such as a physical or electronic
address, without explicit permission
Other conduct which could reasonably be considered inappropriate in a
professional setting
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at joe@masoniteproject.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4
Environment variables in Masonite are defined in a .env
file and should contain all environment variables needed for your project.
You can have multiple environment files that are loaded when the server first starts. It is often helpful to have different variable values depending on the environment where the application is running (locally, during tests or on a production server).
Also it might be useful to have more global environment variables that can be shared across your team for 3rd party services like Stripe or Mailgun and then have more developer specific values like database connections or different storage drivers for development.
We'll walk through how to configure your environments in this documentation.
Environment variables should be set on a project per project basis inside your .env
file.
Never commit any of your .env
files into source control ! It would be a security risk in the event someone gained access to your repository since sensitive credentials and data would get exposed.
That is why .env
and .env.*
are in the project .gitignore
file by default, so you should not worry about accidentally committing those files to source control.
In a fresh Masonite installation, a .env.example
file located at project root directory will define minimum and common configuration values for a Masonite application. During the installation process, this file will be copied to .env
file.
If you have installed Masonite but do not see this .env
file then you can create it manually and copy and paste the contents of .env-example
file.
Environment files are loaded in this order:
Masonite will load the .env
file located at your project root into the Python environment.
Masonite will look for an APP_ENV
variable inside the already loaded .env
. If it is defined it will try to load the .env.{APP_ENV}
file corresponding to this environment name.
For example, if APP_ENV
is set to local
, Masonite will additionally load the .env.local
environment file.
When the server is ready all those variables will be loaded into the current environment ready to be accessed in the different Masonite configuration files or directly with env()
helper.
If some variables contain spaces you should put variable content into double quotes:
You can use Python standard os.getenv()
method to get an environment variable value. It looks like:
Notice that this method does not cast types, so here we got a string instead of a boolean value.
You can also use Masonite helper env
to read an environment variable value. It looks like:
Note that you can provide a default value if the environment variable is not defined. Default value is ""
. For convenience this helper is casting types. Here are different examples of variables type casting:
If you do not wish to cast the value then you can provide a third parameter cast=False
:
The current Masonite environment is defined through the APP_ENV
variable located in your .env
file. You can access it easily through the Masonite app environment()
helper:
When running tests the environment will be set to testing
. You can use is_running_tests()
helper to check if environment is testing
:
You can also check if the environment is a production environment with:
The debug mode is controlled by the APP_DEBUG
environment variable used in config/application.py
configuration file. When crafting a new project, the debug mode is enabled (APP_ENV=True
). It should stay enabled for local development.
When debug mode is enabled all exceptions (or routes not found) are rendered as an HTML debug error page containing a lot of information to help you debug the problem. When disabled, the default 500
, 404
, 403
error pages are rendered.
You can check if debug mode is enabled through the Masonite app is_debug()
helper or with the config
helper:
Never deploy an application in production with debug mode enabled ! This could lead to expose some sensitive configuration data and environment variables to the end user.
The request and the response in Masonite work together to form a well formed response that a browser can read and render. The Response class is responsible for what data is returned and how it is formatted. In this case, a response can have cookies and headers. The Request class can also have cookies and headers. In most times, generally, when you have to fetch headers or cookies you should do so on the request class and when you set cookies and headers you should do so on the response class.
Cookies on the response class are attached to the response and rendered as part of the response headers. Any incoming cookies you would likely fetch from the request class but you can fetch them from the response if you have a reason to:
You can also set cookies on the response:
You can also delete cookies:
You can redirect the user to any number of URL's.
First you can redirect to a simple URL:
You can redirect back to where the user came from:
If using the back method as part of a form request then you will need to use the back view helper as well:
You can also redirect to a route by its name:
This will find a named route users.home
like:
You may also pass parameters to a route if the URL requires it:
Finally you may also pass query string parameters to the url or route to redirect:
You can redirect by flashing a success message into session:
You can redirect by flashing an error message or errors messages into session:
You can directly use validation errors:
You can redirect by flashing form input into session, to re-populate the form when there are errors:
Response headers are any headers that will be found on the response. Masonite takes care of all the important headers for you but there are times where you want to set you own custom headers.
Most incoming headers you want to get can be found on the request class but if you need to get any headers on the response:
Any responses you want to be returned should be set on the response class:
This will set the header on the response.
You can set the status on the response simply:
You can also very simply download assets like images, PDF's or other files:
This will set all the proper headers required and render the file in the browser.
When setting the name, the file extension will be picked up from the file type. This example will download the invoice as the name
invoice-2021-01.pdf
If you want to force download it, or automatically download the file when the response in rendered, you can add a force
parameter and set it to True
:
The request and the response in Masonite work together to form a well formed response that a browser can read and render. The Request class is used to fetch any incoming cookies, headers, URL's, paths, request methods and other incoming data.
Although both the request and response classes have headers and cookies, in most instances, when fetching cookies and headers, it should be fetched from the Request class. When setting headers and cookies it should be done on the response class.
To get cookies you can do so simply:
This will fetch the cookie from the incoming request headers.
You can also set cookies on the request:
Note that setting cookies on the request will NOT return the cookie as part of the response and therefore will NOT keep the cookie from request to request.
Although both the request and response classes have headers and cookies, in most instances, when fetching cookies and headers, it should be fetched from the Request class. When setting headers and cookies it should be done on the response class.
To get a request header you can do so simply:
You can also set a header on the request class:
Note that setting headers on the request will NOT return the header as part of the response.
You can get the current request URI:
You can get the current request method:
You can check if the current path contains a glob style schema:
You can get the current subdomain from the host:
You can get the current host:
Inputs can be from any kind of request method. In Masonite, fetching the input is the same no matter which request method it is.
To fetch an input we can do:
If an input doesn't exist you can pass in a default:
To fetch all inputs from request we can do:
Or we can only fetch some inputs:
To fetch all inputs from request we can do:
If your input is a dictionary you can access nested data in two ways. Take this code example:
You can either access it normally:
Or you can use dot notation to fetch the value for simplicity:
You can also use a * wildcard to get all values from a dictionary list.:
Route parameters are parameters specified in a route and captured from the URL:
If you have a route like this:
You can get the value of the @user_id
parameter like this:
As a convenient way to fetch the user, you can do so directly on the request class if a user is logged in:
If the user is not authenticated then this will be set to None
You can fetch the IP address (ipv4) from which the request has been made by adding the IpMiddleware
to the HTTP middlewares of the project:
Then you can use the ip()
helper:
IP address is retrieved from various HTTP request headers in the following order:
HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR
HTTP_X_FORWARDED
HTTP_X_CLUSTER_CLIENT_IP
HTTP_FORWARDED_FOR
HTTP_FORWARDED
REMOTE_ADDR
The first non-private and non-reserved IP address found by resolving the different headers will be returned by the helper.
The headers used and their order can be customized by overriding the IpMiddleware
and changing the headers
attribute:
Middleware is an extremely important aspect of web applications as it allows you to run important code either before or after every request or even before or after certain routes. In this documentation we'll talk about how middleware works, how to consume middleware and how to create your own middlewar
Middleware classes are placed inside the Kernel
class. All middleware are just classes that contain a before
method and after
method.
There are four types of middleware in total:
Middleware ran before every request
Middleware ran after every request
Middleware ran before certain routes
Middleware ran after certain routes
We have one of two configuration attributes we need to work with. These attributes both reside in our Kernel
file and are http_middleware
and route_middleware
.
http_middleware
is a simple list which should contain your middleware classes. This attribute is a list because all middleware will simply run in succession one after another, similar to Django middleware
In our Kernel
file this type of middleware may look something like:
Middleware will run on every inbound request to the application whether or not a route was found or not.
Route middleware is also simple but instead of a list, it is a dictionary with a custom name as the key and a list of middleware. This is so we can specify the middleware based on the key in our routes file.
In our config/middleware.py
file this might look something like:
By default, all routes inside the
web.py
file will run theweb
middleware list
You can pass parameters from your routes to your middleware in cases where a middleware should act differently depending on your route.
You can do this with a :
symbol next to your route middleware name and then pass in those parameters to the before
and after
middleware methods.
For example, we may be creating a middleware for request throttling and in our routes we have something like this:
notice the throttle:2,100 syntax. The 2 and the 100 will then be passed into the before and after methods of your middleware:
Similiar to the way we can pass values to a middleware using the :
splice we can also use the @
in the value to pass the value of the parameter.
For example, we may create a route and a middleware like this
If we go to a route like /dashboard/152/settings
then the value of 152 will be passed to the middleware before and after methods.
Middleware:
can live anywhere in your project,
Inherit from Masonite's base middleware class
Contain a before and after method that accepts request and response parameters
It's important to note that in order for the request lifecycle to continue, you must return the request class. If you do not return the request class, no other middleware will run after that middleware.
That's it! Now we just have to make sure our route picks this up. If we wanted this to execute after a request, we could use the exact same logic in the after
method instead.
If we are using a route middleware, we'll need to specify which route should execute the middleware. To specify which route we can just append a .middleware()
method onto our routes. This will look something like:
Views in Masonite are a great way to return HTML in your controllers. Views have a hierarchy, lots of helpers and logical controls, and a great way to separate out your business logic from your presentation logic.
By default, all templates for your views are located in the templates
directory. To create a template, simply create a .html
file inside this directory which we will reference to later.
Then inside your controller file you can reference this template:
The first argument here is the name of your template and the second argument should be a dictionary of variables you reference inside your template.
If you have a template inside another directory you can use dot notation to reference those template names
View sharing is when you want a variable to be available in all templates that are rendered. This could be the authenticated user, a copyright year, or anything else you want shared between templates.
View sharing requires you to add a dictionary:
Then inside your template you can do:
Similiar to view sharing, view composing allows you to share templates between specific templates
You may also pass a list of templates:
There are quite a few built in helpers in your views. Here is an extensive list of all view helpers:
You can get the request class:
You can get the location of static assets:
You can create a path to an asset by using the asset
helper:
this will render a URL like this:
See your filesystems.py configuration for how how to set the paths up.
You can create a CSRF token hidden field to be used with forms:
You can get only the token that generates. This is useful for JS frontends where you need to pass a CSRF token to the backend for an AJAX call
You can also get the current authenticated user. This is the same as doing request.user()
.
This will now submit this form as a PUT request.
You can get a route by it's name by using this method:
If your route contains variables you need to pass then you can supply a dictionary as the second argument.
or a list:
This is useful for redirecting back to the previous page. If you supply this helper then the request.back() method will go to this endpoint. It's typically good to use this to go back to a page after a form is submitted with errors:
Now when a form is submitted and you want to send the user back then in your controller you just have to do:
You can access the session here:
This allows you to easily fetch configuration values in your templates:
Gets a cookie:
Get the URL to a location:
To help in debugging, you can use the dd()
helper
Jinja2 allows adding filters to your views. Before we explain how to add filters to all of your templates, let's explain exactly what a view filter is.
Filters can be attached to view variables in order to change and modify them. For example you may have a variable that you want to turn into a slug and have something like:
In Masonite, this slug filter is simply a function that takes the variable as an argument and would look like a simple function like this:
That's it! It's important to note that the variable it is filtering is always passed as the first argument and all other parameters are passed in after so we could do something like:
and then our function would look like:
View tests are simply custom boolean expressions that can be used in your templates. We may want to run boolean tests on specific objects to assert that they pass a test. For example we may want to test if a user is an owner of a company like this:
The code is simple and looks something like this:
That's it! Now we can use the a_company_owner
in our templates just like the first code snippet above!
Notice that we only supplied the function and we did not instantiate anything. The function or object we supply needs to have 1 parameter which is the object or string we are testing.
Environments in views are directories of templates. If you are development packages or building a modular based application, you may have to register different directories for templates. This will allow Masonite to locate your views when referencing them to render. A good place to do this is inside a service provider's register method.
There are 2 separate kinds of loaders.
The first loader is a "package" loader. This is used when registering a package. To do this you can simply register it with the package module path. This will work for most directories that are packages.
The other loader is a FileSystem
loader. This should be used when the directory path is NOT a module but rather just a file system path:
Below are some examples of the Jinja2 syntax which Masonite uses to build views.
It's important to note that Jinja2 statements can be rewritten with line statements and line statements are preferred in Masonite. In comparison to Jinja2 line statements evaluate the whole line, thus the name line statement.
So Jinja2 syntax looks like this:
This can be rewritten like this with line statement syntax:
It's important to note though that these are line statements. Meaning nothing else can be on the line when doing these. For example you CANNOT do this:
But you could achieve that with the regular formatting:
Whichever syntax you choose is up to you.
Note that if you are using an
@
symbol that should not be rendered with Masonite then this will throw an error. An example being when you are using@media
tags in CSS. In this case you will need to wrap this statement inside `` and `
` blocks.
You can show variable or string text by using {{ }}
characters:
If statements are similar to python but require an endif!
Line Statements:
Using alternative Jinja2 syntax:
For loop look similar to the regular python syntax.
Line Statements:
Using alternative Jinja2 syntax:
An include statement is useful for including other templates.
Line Statements:
Using alternative Jinja2 syntax:
Any place you have repeating code you can break out and put it into an include template. These templates will have access to all variables in the current template.
This is useful for having a child template extend a parent template. There can only be 1 extends per template:
Line Statements:
Using alternative Jinja2 syntax:
Blocks are sections of code that can be used as placeholders for a parent template. These are only useful when used with the extends
above. The "base.html" template is the parent template and contains blocks, which are defined in the child template "blocks.html".
Line Statements:
Using alternative Jinja2 syntax:
As you see blocks are fundamental and can be defined with Jinja2 and line statements. It allows you to structure your templates and have less repeating code.
The blocks defined in the child template will be passed to the parent template.
Env Var Value | Casts to (type) |
---|
You will typically do this inside your app (if you don't have one, you should ):
You will typically do this inside your app (if you don't have one, you should ):
Learn more about session in the documentation.
Adding filters is typically done inside your app (if you don't have one, you should ):
In order to do this we need to add a test on the View
class. We can once again do this inside the app
5432 |
|
true |
|
|
"" |
|
True |
|
false |
|
False |
|
smtp |
|
Masonite error handling is based on the ExceptionHandler
class which is responsible for handling all exceptions thrown by your application.
All exceptions are handled by the ExceptionHandler class which is bound to the Service Container through exception_handler
key.
This handler has a logic to decide how to handle exceptions depending on the exception type, the environment type, the request accepted content type and the configured exception handlers.
This handler has by default one driver Exceptionite which is responsible of handling errors in development by providing a lot of context to help debug your application.
When Debug mode is enabled all exceptions are rendered through Exceptionite HTML debug page.
When disabled, the default errors/500.html
, errors/404.html
, errors/403.html
error pages are rendered depending on error type.
Never deploy an application in production with debug mode enabled ! This could lead to expose some sensitive configuration data and environment variables to the end user.
When an exception is raised it will be caught by the ExceptionHandler. Then the following cycle will happen:
A masonite.exception.SomeException
event will be fired.
A specific ExceptionHandler will be used if it exists for the given exception.
Exceptionite will then handle the error by rendering it in the console
and with the Exceptionite JSON response if accepted content is application/json
else with the Exceptionite HTML error page
A masonite.exception.SomeException
event will be fired.
A specific ExceptionHandler will be used if it exists for the given exception. In this case the default ExceptionHandler won't process the exception anymore.
If exception is an HTTP exception it will be handled by the HTTPExceptionHandler which is responsible for selecting an error template (errors/500.html
, errors/404.html
, errors/403.html
) and rendering it.
If exception is a Renderable exception it will be rendered accordingly.
Else this exception has not been yet handled and will be handled as an HTTP exception with 500 Server Error status code.
To report an exception you should simply raise it as any Python exceptions:
There are different type of exceptions.
Simple exceptions will be handled as a 500 Server Error if no custom exceptions handlers are defined for it.
HTTP exceptions are standard exceptions using frequently used HTTP status codes such as 500, 404 or 403.
Those exceptions will be rendered by the HTTPExceptionHandler
with the corresponding status code and the corresponding default error template if it exists in errors/
. (Note that in debug mode this template won't be rendered and the default Exceptionite error page will be rendered instead).
The following HTTP exceptions are bundled into Masonite:
AuthorizationException (403)
RouteNotFoundException (404)
ModelNotFoundException (404)
MethodNotAllowedException (405)
In a default Masonite project, existing errors views are errors/404.html
, errors/403.html
and errors/500.html
. Those views can be customized.
You can also build a custom HTTP exception by setting is_http_exception=True
to it and by defining the get_response()
, get_status()
and get_headers()
methods:
When the above exception is raised, Masonite will look for the error view errors/401.html
in the default project views folder and will render it with a 401
status code. The content of get_response()
method will be passed as the message
context variable to the view.
If this view does not exist the HTML response will be directly the content of the get_response()
method with a 401
status code.
If you want more flexibility to render your exception without using the HTTPExceptionHandler above, you can just add a get_response()
method to it. This method will be given as first argument of response.view()
, so that you can render simple string or your own view template.
Here when raising this exception in your code, Masonite will know that it should render it by calling the get_response()
method and here it will render as a string containing the message raised.
Note that you can also set an HTTP status code by adding - as for HTTP exceptions - the get_status()
method to the exception.
Existing Exception Handlers are:
HTTPExceptionHandler
DumpExceptionHandler
You can build your own Exception Handlers to override the way Masonite handles an exception that is thrown or to add new behaviours.
A handler is a simple class with a handle()
method that Masonite will call when a specific exception is thrown by the application.
As an example, you could handle specifically ZeroDivisionError
exceptions that could be thrown by your application. It will look like this:
The name of the class can be whatever you like. In the handle method you should manually return a response by using the response class.
You will then need to register the class to the container using a specific key binding. The key binding will be {exception_name}Handler
. You can do this in your Kernel file.
To register a custom exception handler for our ZeroDivisionError
we would create a binding that looks like this:
You can add this binding in your AppProvider or in Kernel.py
.
Now when your application throws a ZeroDivisionError
, Masonite will use your handler rather than Masonite's own exception handlers.
If you want to hook up an error tracking service such as Sentry or Rollbar you can do this through event listeners: each time an exception is raised, a masonite.exception.{TheExceptionType}
is fired, allowing to run any custom logic.
First create a listener to run your custom logic:
In a Service Provider you then need to register this listener:
Configuration files in Masonite are gathered in one folder named config
in default projects.
Each feature can have some options in its own file named after the feature. For example you will find mail related options in config/mail.py
file.
The Configuration
class is responsible for loading all configuration files before the application starts.
It will load all files located at path defined through config.location
binding which default to config/
.
Then values are accessed based on the file they belong to and a dotted path can be used to access nested options.
Given the following config/mail.py
file:
Accessing mail
will return a dictionary with all the options.
Accessing mail.from_email
will return the FROM_EMAIL
value
Accessing mail.drivers.smtp.port
will return the port value for smtp driver.
To read a configuration value one can use the Config
facade:
or the config
helper:
Setting configuration values is achieved through projet configuration files.
However, you can override on the fly a configuration value with the Config
facade:
This should be done sparingly as this could have unexpected side effects depending at which time you override the configuration option.
This is mostly useful during tests, when you want to override a configuration option to test a specific behaviour:
But if you simply want to have different configuration depending on the environment (development, testing or production) you should rely instead on environment variables used to define configuration options.
Masonite also provides a simple way to authorize user actions against a given resource. This is achieved with two concepts: gates and policies. Gates are as the name suggests an authorization check that you will be able to invoke to verify user access. Policies are a way to groupe authorization logic around a model.
Gates are simple callable that will define if a user is authorized to perform a given action. A handy Gate
facade is available to easily manipulate gates.
Gates receive a user instance as their first argument and may receive additionals arguments such as a Model instance. You needs to define Gates
in boot()
method of your service provider.
In the following example we are adding gates to verify if a user can create and update posts. Users can create posts if they are administrators and can update posts they have created only.
You can then check if a gate exists or has been registed by using has()
which is returning a boolean:
If a unknown gate is used a GateDoesNotExist
exception will be raised.
Then anywhere (often in a controller) in your code you can use those gates to check if the current authenticated user is authorized to perform the given action defined by the gate.
Gates exposes different methods to perform verification: allows()
, denies()
, none()
, any()
, authorize()
inspect()
.
allows()
, denies()
, none()
and any()
return a boolean indicating if user is authorized
authorize()
does not return a boolean but will raise an AuthorizationException
exception instead that will be rendered as an HTTP response with a 403 status code.
Finally for better control over the authorization check you can analyse the response with inspect()
:
Authorizes
class can be added to your User model to allow quick permission checks:
A fluent authorization api will now be available on User
instances:
All of those methods receive the gate name as first argument and then some additional arguments if required.
You can use the for_user()
method on the Gate facade to make the verification against a given user instead of the authenticated user.
During the gate authorization process, before
and after
hooks can be triggered.
A before
hook can be added like this:
The after
hook works the same way:
If the after
callback is returning a value it will take priority over the gate result check.
Policies are classes that organize authorization logic around a specific model.
You can run the craft command:
You can also create a policy with a set of predefined gates by using the --model
flag:
A model policy comes with common actions that we can perform on a model:
You are free to add any other methods on your policies:
Then in your service provider (as for defining gates) you should register the policies and bind them with a model:
An example policy for the Post model may look like this:
If an unknown policy is used then a PolicyDoesNotExist
exception will be raised.
You can then use the Gate
facade methods to authorize actions defined in your policies. With the previously defined PostPolicy
we could make the following calls:
The create()
or view_any()
methods do not take a model instance, that is why the model class should be provided so that Gate mechanism can infer from which policy those methods are belonging to.
Masonite makes authentication really simply.
Masonite comes with a command to scaffold out a basic authentication system. You may use this as a great starting point for adding authentication to your application. This command will create controllers, views, and mailables for you.
If you would like to implement your own authentication from scratch you can skip to the sections below.
First run the command to add the news files:
Then add the authentication routes to your routes file:
You may then go to the /login
or /register
route to implement your authentication.
The configuration for Masonite's authentication is quite simple:
The default key here is the guard to use for authentication. The web
dictionary is the configuration for the web guard.
You can attempt a login by using the Auth
class and using the attempt
method:
If the attempt succeeds, the user will now be authenticated and the result of the attempt will be the authenticated model.
If the attempt fails then the result will be None
.
If you know the primary key of the model, you can attempt by the id:
You can logout the current user:
You can get the current authenticated user:
If the user is not authenticated, this will return None
.
You can register several routes quickly using the auth class:
This will register the following routes:
Guards are encapsulated logic for logging in, registering and fetching users. The web guard uses a cookie
driver which sets a token
cookie which is used later to fetch the user.
You can switch the guard on the fly to attempt authentication on different guards:
Adding API Support to your Masonite project is very simple. Masonite comes with supporting for returning JSON responses already but there are a few API features for authenticating and authorizing that are helpful.
Default projects don't come with these features so you'll have to simply register them.
First, register the ApiProvider in your project by adding the service provider to your PROVIDERS list:
This will register some required classes used for API development.
You should now create an api.py
file for adding your API routes to:
You will then have to add a binding to the container for the location of this file. You can do so in your Kernel.py
file inside the register_routes
method:
Any routes inside this file will be grouped inside an api middleware stack.
Next, you must choose a model you want to be responsible for authentication. This is typically your User model. You will have to inherit the AuthenticatesTokens
class onto this model:
This will allow the model to save tokens onto the table.
Next you will add a column to the models table for saving the token. Here we are naming it api_token
but this is configurable by adding a __TOKEN_COLUMN__
attribute to your model. Your migration file should look like this:
Then migrate your migrations by running:
Next, you can create a new API config file. You can do so simply by running the install command:
This will create a new api.py
config file in your configuration directory that looks like this:
This will attempt to import your user model but if you have a different model or if its in a different location you can change it in that model.
This command will also generate a secret key, you should store that secret key in an environment variable called JWT_SECRET
. This will be used as a salt for encoding and decoding the JWT token.
The authenticates
key is used as a check to check against the database on every request to see if the token is set on the user. By default, the database is not called to check if the token is assigned to a user. One of the benefits of JWT is the need to not have to make a database call to validate the user but if you want that behavior, you can set this option to True
You should add some routes to your web.py
file which can be used to authenticate users to give them JWT tokens:
The above parameters are the defaults already so if you don't want to change them then you don't have to specify them.
Since the routes in your api.py
file are wrapped in an api
middleware, you should add a middleware stack in your route middleware in your Kernel file:
This middleware will allow any routes set on this stack to be protected by JWT authorization.
By default, all routes in the
routes/api.py
file already have theapi
middleware stack on them so there is no need to specify the stack on all your API routes.
Once these steps are done, you may now create your API's
Once the setup is done, we may start building the API.
One of the ways to build an API is using controller and route resources.
A controller resource is a controller with several methods on them used to specify each action within an entity in the application (like users).
To create a controller resource you can run the controller command with an -a
flag:
This will create a controller with the following methods:
index
show
store
update
destroy
You can then create a route resource:
This will create the below routes which will match up with your API Controller methods:
The routes we added earlier contain 2 authentication methods. The /api/auth
route can be used to get a new authentication token:
First, send a POST
request with a username
and password
to get back a JWT token:
You should then send this token with either a token
input or a Authorization
header:
If you do not set a value for the expires
key in the configuration file then your JWT tokens will not expire and will be valid forever.
If you do set an expiration time in your configuration file then the JWT token will expire after that amount of minutes. If the token expires, you will need to reauthenticate to get a new token. This can be done easily by sending the old token to get back a new one:
You can do this by sending a POST request to /api/reauth
with a token
input containing the current JWT token. This will check the table for the token and if found, will generate a new token.
One of the issues with JWT tokens is there is little that can be done to invalidate JWT tokens. Once a JWT token is valid, it is typically valid forever.
One way to invalid JWT tokens, and force users to reauthenticate, is to specify a version. A JWT token authenticated will contain a version number if one exists. When the JWT token is validated, the version number in the token will check against the version number in the configuration file. If they do not match then the token is considered invalid and the user will have to reauthenticate to get back a new token.
Since we store the active api_token on the table we are able to retrieve the user using the LoadUserMiddleware
and a new guard
route middleware stack:
First add the guard middleware stack and add the LoadUserMiddleware
to the api
stack.
Lastly, in your route or route groups you can specify the guard middleware and specify the guard name:
Masonite ships with a "pub and sub" style events feature that allows you to subscirbe to various events and run listeners, or additional logic, when those events get emitted.
The first step in events is creating an event to listen to.
Events are simple classes that you can create wherever you like:
This will create a simple class we can later emit.
You can also fire events without an Event class. The event will just be a specific key you can listen to.
The listener will run the logic when the event is emitted. You can create as many listeners as you like and register as many listeners to events as you need to.
To create a listener simply run the command:
This will create a class like this:
The handle method will run when the listener runs. It will pass the event as the first parameter and any additional arguments that are emitted from the event as additional parameters.
After your events and listeners are created you will need to register them to the event class.
You can also listen to events without Event classes:
Using event strings allow to use wildcard event listening. For example if the application is emitting multiple events related to users such as users.added
, users.updated
and users.deleted
you can listen to all of those events at once:
To fire an event with an event class you can use the fire
method from the Event
class:
To fire a simple event without a class you will use the same method:
As an example, to build a listener that sends an email:
First, create the listener:
Then we can build out the listener.
To send an email we will need to import the mailable class and send the email using the mail
key from the container:
You can then register the event inside the provider:
When you emit the UserAdded
event inside the controller, or somewhere else in the project, it will now send this email out.
You can register as many listeners to the events as you like by simply adding more listeners to the list.
Commands in Masonite are generally designed to make your development life easier. Commands can range from creating controllers and models to installing packages and publishing assets.
Masonite uses the package for shell command feature.
Available commands for Masonite can be displayed by running:
This will show a list of commands already available for Masonite.
Every command has a documentation screen which describes the command's available arguments and options. In order to view a command documentation, prefix the name of the command with help
. For example, to see help of serve
command you can run:
Commands can be created with a simple command class inheriting from Masonite Command
class:
Command's name, description and arguments are parsed from the Command docstring.
The docstring should start with the description of the command
and then after a blank line you can define the command name.
After the name of the command in the docstring should come the arguments. Arguments are defined with one indent and enclosed into brackets.
Positional (mandatory) arguments are defined without dashes (-
or --
).
Here is how to define a positional argument called name
with a description:
Inside the command, positional arguments can be retrieved with self.argument(arg_name)
Optional arguments are defined with dashes and can be used in any order in the command call. An optional argument --force
can have a short name --f
.
Here is how to define two optional arguments iterations
and force
with a description:
Notice how we provided the short version for the force
argument but not for the iterations
arguments
Now the command can be used like this:
If the optional argument is requiring a value you should add the =
suffix:
Here when using iterations
, the user should provide a value.
If the argument may or may not have a value, you can use the suffix =?
instead.
Finally if a default value should be used when no value is provided, add the suffix ={default}
:
Inside the command, optional arguments can be retrieved with self.option(arg_name)
You can print messages to console with different formatting:
self.info("Info Message")
: will output a message in green
self.warning("Warning Message")
: will output a message in yellow
self.error("Error Message")
: will output a message in bold red
self.comment("Comment Message")
: will output a message in light blue
Masonite Command
class is inheriting Cleo Command
class so you should be able to use all Cleo features when creating commands.
add()
method takes one or multiple commands:
When you run python craft
you will now see the command you added.
Masonite uses Laravel Mix which provides a really simple way to handle asset compiling even greater than simple SASS and LESS. You don't need to be an expert in either Laravel Mix or NPM to compile assets, though.
To get started we can simply run NPM install:
This will install everything you need to start compiling assets.
The configuration settings will be made inside your webpack.mix.js
file located in the root of your project.
You can see there is already an example config setup for you that looks like this:
This will move these 2 files, resources/js/app.js
and resources/css/app.css
and compile them both into the storage/compiled
directory.
Feel free to change which directories the files get compiled to. For more information on additional configuration values take a look at the
Laravel is using Laravel mix so you can just follow guidelines to setup TailwindCSS for Laravel Mix on .
Now that we have our compiled assets configured we can now actually compile them.
You can do so by running:
This will compile the assets and put them in the directories you put in the configuration file.
You can also have NPM wait for changes and recompile when changes are detected in frontend files. This is similiar to an auto reloading server. To do this just run:
Laravel Mix can take care of file hashing when releasing assets to production, by adding a hash suffix to your assets to automatically bust cache when loading assets. To enable this you can add the following in your webpack.mix.js
file:
After Laravel Mix compiled your assets you won't be able to know the exact filename (because of the hash) you should use in your views to reference your assets. You can use Masonite mix()
helper to resolve the correct file path to your assets.
Masonite provides a powerful caching feature to keep any data cached that may be needless or expensive to fetch on every request. Masonite caching allows you to save and fetch data, set expiration times and manage different cache stores.
Masonite supports the following cache drivers: , and a basic driver.
We'll walk through how to configure and use cache in this documentation.
Cache configuration is located at config/cache.py
file. In this file, you can specify different cache stores with a name via the STORES
dictionary and the default to use in your application with the default
key.
Masonite is configured to use the File cache driver by default, named local
.
For production applications, it is recommended to use a more efficient driver such as Memcached
or Redis
.
File cache driver is storing data by saving it on the server's filesystem. It can be used as is without third party service.
Location where Masonite will store cache data files defaults to storage/framework/cache
in project root directory but can be changed with location
parameter.
Redis cache driver is requiring the redis
python package, that you can install with:
Then you should define Redis as default store and configure it with your Redis server parameters:
Memcached cache driver is requiring the pymemcache
python package, that you can install with:
Then you should define Memcached as default store and configure it with your Memcached server parameters:
Two methods are available: add
and put
.
You can easily add cache data using theadd
method. This will either fetch the data already in the cache, if it is not expired, or it will insert the new value.
If age
key exists in the cache AND it is not expired, then "21" will be added to the cache and returned. If the age
key does not exist or is not expired then it will return whatever data is in the cache for that key.
The put
method will put data into the cache regardless of if it exists already. This is a good way to overwrite data in the cache:
You can specify the number of seconds that the cache should be valid for. Do not specify any time or specify None
to keep the data forever.
You may also cache lists and dictionaries which will preserve the data types:
You can get cache data from the cache. If the data is expired then this will either return None
or the default you specify:
This will either fetch the correct age data from the cache or return the default value of 40
.
You can also see if a value exists in the cache (and it is not expired):
If you want to forget an item in the cache you can:
This will remove this item from the cache.
You can increment and decrement a value if it is an integer:
Remembering is a great way to save something to a cache using a callable:
To delete everything in the cache you can simply flush it:
CSRF protection typically entails setting a unique token to the user for that page request that matches the same token on the server. This prevents any person from submitting a form without the correct token. There are many online resources that teach what CSRF does and how it works but Masonite makes it really simple to use.
The CSRF features for Masonite are located in the CsrfProvider
Service Provider and the CsrfMiddleware
. If you do not wish to have CSRF protection then you can safely remove both of these.
The CsrfProvider
simply loads the CSRF features into the container and the CsrfMiddleware
is what actually generates the keys and checks if they are valid.
By default, all POST
requests require a CSRF token. We can simply add a CSRF token in our forms by adding the {{ csrf_field }}
tag to our form like so:
This will add a hidden field that looks like:
If this token is changed or manipulated, Masonite will throw an InvalidCsrfToken
exception from inside the middleware.
If you attempt a POST
request without the {{ csrf_field }}
then you will receive a InvalidCsrfException
exception. This just means you are either missing the Jinja2 tag or you are missing that route from the exempt
class attribute in your middleware.
You can get also get the token that is generated. This is useful for JS frontends where you need to pass a CSRF token to the backend for an AJAX call
For ajax calls, the best way to pass CSRF tokens is by setting the token inside a parent template inside a meta
tag like this:
And then you can fetch the token and put it wherever you need:
You can then pass the token via the X-CSRF-TOKEN
header instead of the __token
input for ease of use.
Not all routes may require CSRF protection such as OAuth authentication or various webhooks. In order to exempt routes from protection we can add it to the exempt
class attribute in the middleware located at app/http/middleware/CsrfMiddleware.py
:
Now any POST routes that are to your-domain.com/oauth/github
are not protected by CSRF and no checks will be made against this route. Use this sparingly as CSRF protection is crucial to application security but you may find that not all routes need it.
You can also use *
wildcards for exempting several routes under the same prefix. For example you may find yourself needing to do this:
This can get a bit repetitive so you may specify a wildcard instead:
Masonite comes with a powerful way to broadcast events in your application. These events can be listened to client-side using Javascript.
These can be things like a new notification which you can show on the frontend without reloading the page.
Masonite comes with one server-side driver: .
Server side configuration for broadcasting is done in config/broadcast.py
configuration file. For now there is one driver available .
You should create an account on Pusher Channels and then create a Pusher application on your account and get the related credentials (client, app_id, secret) and the cluster location name and put this into the broadcast pusher options.
Finally make sure you install the pusher
python package
Include the pusher-js script tag on your page
Create a Pusher instance configured with your credentials
Now you're ready to subscribe to channels and listen for channels events.
Broadcast events are simple classes that inherit from CanBroadcast
. You may use any class, including the Masonite Event classes.
A broadcast event will look like this:
Note that the event name emitted to the client will be the name of the class. Here it would be UserAdded
.
You can broadcast the event using the Broadcast
facade or by resolving Broadcast
class from container.
You can broadcast easily without creating a Broadcast event
Or you can broadcast the event class created earlier
You may broadcast on multiple channels as well:
This type of broadcasting will emit all channels as public. For private and presence channels, keep reading.
To listen for events on client-side you must first subscribe to the channel the events are emitted on
Then you can listen for events
Different channel types are included in Masonite.
Inside the event class you can specify a Public channel. These channels allow anyone with a connection to listen to events on this channel:
Private channels require authorization from the user to connect to the channel. You can use this channel to emit only events that a user should listen in on.
Private channels are channels that start with a private-
prefix. When using private channels, the prefix will be prepended for you automatically.
Private channels can only be broadasting on if users are logged in. When the channel is authorized, it will check if the user is currently authenticated before it broadcasts. If the user is not authenticated it will not broadcast anything on this channel.
This will emit events on the private-channel_name
channel.
On the frontend, when you make a connection to a private channel, a POST request is triggered by the broadcast client to authenticate the private channel. Masonite ships with this authentication route for you. All you need to do is add it to your routes:
This will create a route you can authenticate you private channel on the frontend. The authorization route will be /broadcasting/authorize
but you can change this to anything you like:
You will also need to add the /pusher/user-auth
route to the CSRF exemption.
The reason for this is that the broadcast client will not send the CSRF token along with the POST authorization request.
The default behaviour is to authorize everyone to access any private channels.
If you want to customize channels authorization logic you can add your own broadcast authorization route with a custom controller. Let's imagine you want to authenticate channels per users, meaning that user with ID 1
will be able to authenticate to channel private-1
, user with ID 2
to channel private-2
and so on.
First you need to remove Broadcast.routes()
from your routes and add your own route
Then you need to create a custom controller to implement your logic
Presence channels work exactly the same as private channels except you can see who else is inside this channel. This is great for chatroom type applications.
For Presence channels, the user also has to be authenticated.
This will emit events on the presence-channel_name
channel.
To get started more easily with event broadcasting in Masonite, two small examples are available here:
To do this we need to create a NewRelease
Broadcast event and trigger this event from the backend.
On the frontend we need to listen to releases
channel and subscribe to NewRelease
events to display an alert box with the release message.
Let's imagine our User model has two roles basic
and admin
and that we want to send alerts to admin users only. The basic users should not be authorized to subscribe to the alerts.
To achieve this on the backend we need to:
create a custom authentication route to authorize admin users only on channel private-admins
create a AdminUserAlert
Broadcast event
trigger this event from the backend.
Let's first create the authentication route and controller
On the frontend we need to listen to private-admins
channel and subscribe to AdminUserAlert
events to display an alert box with the message.
You're ready to start broadcasting events in your app !
URI | Description |
---|---|
Method | URL | Action | Route Name |
---|---|---|---|
You can do this via the AppProvider
or a you will create yourself:
You can find more information and more features for creating commands in .
Once created you can register the command to Masonite's inside a (if you don't have one, you should ):
Please follow the guidelines directly on
More information on this feature in .
Finally ensure that the Redis server is running and you're ready to .
Finally ensure that the Memcached server is running and you're ready to .
You can access Cache service via the Cache
facade or by resolving it from the .
To be able to receive broadcast events in the browser you should install .
This is the quickest way to install Pusher. But for a real app you will often use a build system to and will install the Javascript Pusher SDK with npm install pusher-js
and then import Pusher class with import Pusher from 'pusher-js';
.
It is advised to use environment variables instead of hard-coding credentials client-side. If you're using Laravel Mix to then you should prefix your environment variables with MIX_
.
In this section we will use the client pusher
instance .
Pusher is expecting the authentication route to be /pusher/user-auth
. If you want to change this client-side you can do it when creating Pusher instance
If you want to keep CSRF protection you can read more about it .
Adding the authentication route is the same as for .
Authorizing channels is the same as for .
Sending public app releases notification to every users (using )
Sending private alerts to admin users (using )
GET /login
Displays a login form for the user
POST /login
Attempts a login for the user
GET /home
A home page for the user after a login attempt succeeds
GET /register
Displays a registration form for the user
POST /register
Saved the posted information and creates a new user
GET /password_reset
Displays a password reset form
POST /password_reset
Attempts to reset the users password
GET /change_password
Displays a form to request a new password
POST /change_password
Requests a new password
GET
/users
index
users.show
GET
/users/@id
show
users.show
POST
/users
store
users.store
PUT/PATCH
/users/@id
update
users.update
DELETE
/users/@id
destroy
users.destroy
Masonite has several helpers available. Many helpers are used internally to help write clean Masonite code but can be used in your projects as well.
All helpers can be imported from helpers
module.
Get easily access to application container:
You can also resolve dependencies directly and even pass arguments when resolving:
You can easily dump variables into console for debugging, from inside a controller for example: For this you can use Dump facade or the built-in dump
python method:
This will dump data in console in a nice format to ease debugging.
If you want the code to stop and renders a dump page instead you can use the dump and die helper named dd
:
This will stop code at this line and renders a nice dump page where you can see all variables dumped until now.
Note that dumps will accumulate into session. If you want to clear dumps, you can use Dump.clear()
or you can enable the HTTP middleware ClearDumpsBetweenRequestsMiddleware
to clear dumps between every requests.
TODO
TODO
Get the absolute path to your project root directory or build the absolute path to a given file relative to the project root directory.
Get the absolute path to your project views directory or build the absolute path to a given file relative to the project views directory.
Get the absolute path to your project controllers directory or build the absolute path to a given file relative to the project controllers directory.
Get the absolute path to your project mailables directory or build the absolute path to a given file relative to the project mailables directory.
Get the absolute path to your project config directory or build the absolute path to a given file relative to the project config directory.
Get the absolute path to your project migrations directory or build the absolute path to a given file relative to the project migrations directory.
Get the absolute path to your project seeds directory or build the absolute path to a given file relative to the project seeds directory.
Get the absolute path to your project jobs directory or build the absolute path to a given file relative to the project jobs directory.
Get the absolute path to your project resources directory or build the absolute path to a given file relative to the project resources directory.
Get the absolute path to your project models directory or build the absolute path to a given file relative to the project models directory.
All above paths helper return an absolute path to the location. When providing a file path to the helper you can set absolute=False
to get the path relative given directory.
The Url helper allows you to create a full path URL:
The URL will come from the APP_URL
in your application config file.
It accepts a dictionary to add query string parameters when building the url:
You can also generate a URL for an asset:
You can also generate a URL for a route by its route name:
You can also generate just a path:
It accepts a dictionary to add query string parameters when building the route url:
TODO
The compact helper is a shortcut helper when you want to compile a dictionary from variables.
There are times when you will have instances like this:
Notice we repeated the users
and articles
key the same as the variables name. In this case we can use the compact
helper to clean the code up a bit:
The optional helper takes an object and allows any method calls on the object. If the method exists on the object it will return the value or else it will return None:
You may have a peice of code that looks like this:
with the optional helper you can condense the code into something like this:
TODO
TODO
Facades are an easy way to access the Service Container classes without making them from the Service Container.
Facades are just a shortcut to resolve a key in the Service Container. Instead of doing:
you can do:
To import any built-in facades you can import them from the masonite.facades
namespace:
Masonite ships with several facades you can use out of the box:
Auth
Broadcast
Config
Dump
Gate
Hash
Loader
Mail
Notification
Queue
Request
Response
Session
Storage
View
Url
To create your own facade is simple:
Then import and use your facade:
To benefit from code intellisense/type hinting when using Facade (e.g. in VSCode editor) you should create a .pyi
file just next to your facade file class to add type hinting to your new facade.
Then in this facade you should declare your typed methods. Here is a partial example of the Mail facades types hints:
Notice that:
methods code is not present, but replaced with ...
.
methods do not receive self
as first argument. Because when calling the facade we don't really instantiate it (even if we get an instance of the object bound to the container). This allow to have the correct type hint behaviour.
The Masonite hashing feature is a very useful feature to prevent exposing ID's in your application.
Many times you need to expose your database primary keys to the frontend. For example, when updating a record, you might need to pass in a primary key value to a URL like /users/10/settings
.
Typically you want to hide these key values from a hacker trying to change these values easily.
With the Masonite hashing feature you can change a value like 10
to l9avmeG
and prevent exposing those sensitive integer values.
The Masonite hashing feature automatically decodes values before they get to the controller. To do this it you need to specify both a middleware to help decode the values as well as the provider to register the helper in the templates.
For the middleare you can add it easily:
You should put the Hash ID middleware towards the top of the middleware stack so it will decode the request properly before getting to the other middleware in the stack.
The provider should also be added:
This will register a template helper and some other useful features.
You can use the helper directly to encode or decode integers easily:
Inside your templates you can use the hashid
template helper:
When submitted to the backend the will now be the normal integer value of the user id:
When using the template helper, you may also use the hashid feature for request params:
If you have a route like this:
Inside your controller you are able to get the unhashed request parameter:
Creating packages is very simple for Masonite. You can create a package and publish it on PyPi in less than 5 minutes. With Masonite packages you will scaffold and centralize some features to reuse it all your Masonite projects with ease. Masonite comes with several helper functions in order to create packages which can add configuration files, routes, controllers, views, commands, migrations and more.
As a developer, you will be responsible for both making packages and consuming packages. In this documentation we'll talk about both. We'll start by talking about how to make a package and then talk about how to use that package or other third party packages.
You can browse Masonite packages (official and community) on: packages.masoniteproject.com (alpha version).
Masonite, being a Python framework, you can obviously use all Python packages that aren’t designed for a specific framework. For example, you can obviously use a library like requests
but you can’t use specific Django Rest Framework.
Package providers are the connection between your package and Masonite. A service provider is responsible for binding things into Masonite container and specifying from where to load package resources such as views, configuration, routes and assets.
Your Masonite project will discover packages through the PROVIDERS
list defined in your providers.py
configuration file. When a package provider is added to this list, this will allow additional bindings, commands, views, routes, migrations and assets to be registered in your project.
Keep in mind that some simple packages do not need to register resources in your project though.
We provide two ways to quickly scaffold your package layout:
starter-package GitHub template
The starter-package is just a GitHub template so you only have to click Use this template
to create your own GitHub repository scaffolded with the default package layout and then clone your repository to start developing locally.
The cookiecutter template is using cookiecutter
package to scaffold your package with configuration options (name, author, url...). The advantages is that you won't need to edit all the occurences of your package name after generation.
Install cookiecutter
globally (or locally) on your computer:
Then you just have to run (in the directory where you want your package repository to be created):
You can now start developing your package !
There are man ways to create a virtual environment in Python but here is a simple way to do it:
Activating your virtual environment on Mac and Linux is simple:
or if you are on Windows:
The default package layout contains a Makefile that help getting started quickly. You just have to run:
This will install Masonite, development tools such as flake8
and pytest
and it will also install your package locally so that you can start using and testing it in the test project directly.
Now you can run the tests to make sure everything is working properly:
The default package layout come with one basic test. You should see this test passing. You can then start building your package and adding more unit tests.
The default package layout comes with a test project located in tests/integrations
. This project is really useful to directly test your package behaviour. It is scaffolded as a default Masonite project with your package already installed (see Installing a Package section). You can run the project with the usual command:
You can then visit http://localhost:8000
to see the welcome page.
When you make changes to your package as your package is installed locally and registered into your project, your changes will be directly available in the project. You will just need to refresh your pages to see changes.
If your package has migrations and you want to migrate your test project you should first register your migrations, publish them and then run the usual migrate
command:
Once you're satisfied with your package it's time to release it on PyPi so that everyone can install it. We made it really easy to do this with the make commands.
Registering on PyPi
If you never uploaded a package on PyPi before you will need to register. Verify your email address.
Creating a publish token
API tokens provide an alternative way (instead of username and password) to authenticate when uploading packages to PyPI. It is is strongly recommended to use an API token where possible.
Go to your PyPi account and find the API tokens
section. Click on Add API token
, give a significant name such as publish-token
and create the token. Write down the token key somewhere for later.
Configuring PyPi on your computer
If you never uploaded a package on PyPi before you will need to configure PyPi on your computer. For this you will need to add a .pypirc
file in your home directory. If you do not have one then you can easily creating one using:
This will move the file to your home directory. If you are using Windows you may need to move this file manually.
Then fill in password
key with the token you created later prefixed by pypi-
. With a token starting with AgEIcHlwaS5vcmcCJGNjYjA4M...
the .pypirc
file would look like:
Publishing the package to PyPI
Now you're ready to upload your package to PyPI. Ensure that all parameters of setup.py
file are up to date. It's the file describing your package. Fill in the correct version number you want to publish. When ready you can run:
This will install twine
if not installed yet, build the package, upload it to PyPi and delete the build artifacts. You should then see a success message and be able to browse your package on PyPi.
You should always check that the package name is available on PyPi and that the version number to publish has not been published before. Else you won't be able to publish your package.
Make the package available on masonite packages list
To make your package available on packages.masoniteproject.com (alpha version) you need to add Framework :: Masonite
classifier in setup.py
:
You can find more information on the website FAQ.
When developing a package you might need to use a configuration file, to add migrations, routes and controllers or views. All those resources can be located in your package but at one point a user might want to override it and will need to publish those resources locally in its project.
The following section will explain how to register those resources in your package to be used in a Masonite project and how to make those resources publishable.
Masonite makes it really easy to do this by creating a specific package provider that will register your package resources. The default package layout comes with such a provider inheriting from PackageProvider
class:
configure()
method is called in usual register()
method and is used to register all resources used in your package.
root(import_path)
method should be called first and is used to specify the import path of your package. If your package needs to be used like this:
Then super_awesome_package
is the import path of your package. If your package is imported like this:
Then masonite.inertia
is the import path of your package.
name(string)
method should be called in second and is used to specify the name of your package (not the PyPi package name neither the Python module name) but the name that will be used to reference your package in the publish command or in the resources paths (it should be a name without special characters and spaces i.e. a Python valid name). This will also be the name of your package configuration file.
Your package is likely to have a configuration file. You will want to make your package configuration available through the handy config()
Masonite helper. For this you will need to call config(path, publish=False)
inside configure()
method:
This will load the package configuration file located at super_awesome_package/config/super_awesome.py
into Masonite config. The configuration will then be available with config("super_awesome.key")
.
If you want to allow users to publish the configuration file into their own project you should add publish=True
argument.
The package publish command will publish the configuration into the defined project configuration folder. With the default project settings it would be in config/super_awesome.py
.
Configuration values located in packages and in local project will be merged. Values defined locally in the project takes precedance over the default values of the package.
If your package contains migrations you can register the migration files to be published in a project:
The package publish command will publish the migrations files into the defined project migrations folder. With the default project settings it would be in databases/migrations/
. Migrations file are published with a timestamp, so here it would result in those two files: {timestamp}_create_some_table.py
and {timestamp}_create_other_table.py
.
If your package contains routes you can register them by providing your route files and the locations to load controllers (used by your routes) from. For this you will need to call controllers(*locations)
and then routes(*routes)
inside configure()
method.
If your routes are defined in super_awesome_package/routes/api.py
and super_awesome_package/routes/web.py
and the controllers files available in super_awesome_package/controllers
you can do:
Now Masonite should be able to resolve new routes from your packages.
If your package contains views you can register them by providing folders containing your views. For this you will need to call views(*folders, publish=False)
inside configure()
method. The views will be namespaced after your package name:
For example if your package contains an admin
folder located at super_awesome_package/admin/
containing a index.html
view you can do:
Views will be available in controllers:
If you want to allow users to publish the view file into their own project so they can tweak them you should add publish=True
argument. The package publish command will publish the views files into the defined project views folder. With the default project settings it would be in templates/vendor/super_awesome/admin/index.html
.
If your project contains assets (such as JS, CSS or images files) you can register them to be published in the project by calling assets(*paths)
inside configure()
method.
For example if your package contains an assets
folder located at super_awesome_package/assets/
containing some asset files and folders you can do:
The package publish command will publish the assets into the defined project resources folder. With the default project settings it would be in resources/vendor/super_awesome/
.
If your project contains commands you will want to register it when your package is installed in a project so that we can run python craft my_package_command
. For this you will need to call commands(*command_class)
inside configure()
method.
Now when you run python craft
you should see the two registered commands.
When using PackageProvider
class to create your package service provider, you will be able to publish all package resources defined below in a project. You just need to run the command package:publish
with the name of the package (declared inside configure()
method). With our example it would be:
If you want to publish some specific resources only, you can use --resources
flag:
Here this will only publish configuration and views into your project.
Finally if you want to check what resources a package can publish you just need to run:
This will output the list of resources that the package is going to publish into your project.
If the package has been released on PyPi you need to install it as any Python package:
Then you should follow package installation guidelines but often it will consist in:
registering the package Service Provider in your project:
publishing some files if you need to tweak package resources or configuration:
You should be ready to use the package in your project !
Masonite Tinker is a powerful REPL (Read, Evaluate, Print and Loop) environment for the Masonite framework. It's a supercharged Python interactive shell with access to the container, models and helpers.
Tinker allows you to interact with your entire Masonite project on the command line, including models, jobs, events, and more. To enter the Tinker environment, run the tinker command:
This will open a Python shell with the application container (under the app
variable), the application models and some helpers imported for you.
Finally you can get an enhanced experience by using the Tinker IPython shell. IPython is an improved Python shell offering some interesting features:
Syntax highlighting
Tab completion of python variables and keywords, filenames and function keywords
Input history, persistent across sessions
Integrated access to the pdb debugger and the Python profiler
and much more...
You just need to use -i
option and install IPython if not installed yet (pip install IPython
):
By default your app models are loaded from the location configured in your project Kernel. You can override the directory to load models from with the -d
flag. It should be a path relative to your project root. For example you can run the following command if your models are located in a models/
folder located at your project root:
You can use PYTHONSTARTUP
environment variable to add a script that you want to run at the beginning of the shell session.
With IPython you can use this variable or put some Python scripts in ~/.ipython/profile_default/startup/
. IPython will run those scripts for you at the beginning of the shell session.
Masonite comes with a simple way to store sessions. Masonite supports the following session drivers: Cookie and Redis.
Session configuration is located at config/session.py
file. In this file, you can configure which driver to use.
Masonite is configured to use the Cookie session driver by default, named cookie
.
Cookie driver will store all session data in the users cookies. It can be used as is.
Redis driver is requiring the redis
python package, that you can install with:
Then you should define Redis as default driver and configure it with your Redis server parameters:
Finally ensure that the Redis server is running and you're ready to start using sessions.
To save session data you can simply "set" data into the session:
Flash data is data specific to the next request. This is useful for data such as error messages or alerts:
To get back the session data you set you can simply using the "get" method:
You can check if a session has a specific key:
You can also delete a key from the session
You can reset all data in a session:
Masonite comes with a simple way to upload files to different file systems.
In Masonite, a Filesystem is a place where assets are stored. These locations could be somewhere local on the server or in a cloud storage service like Amazon S3.
The configuration for the filesystem feature is broken up into "disks". These "disks" or connection configurations can be any name you want.
Here is an example configuration:
The default configuration is the name of the disk you want to be used as the default when using the Filesystem features.
The local driver is used for local filesystems like server directories. All files are stored and managed "locally" on the server.
The S3 driver is used for connecting to Amazon's S3 cloud service.
Uploading files is simple. You will have to use the Masonite Storage
class.
The first and most simplest method is taking a file and putting text into it. For this we can use the put
method
When uploading files from a form you will find the put_file
method more useful:
The put_file
method will return the relative path to the file so you can save it to the database and fetch it later.
By default, a file name will be auto generated for you using a UUID4 string. You can specify your own name by using a name
parameter:
You do not need to specify the extension in the name as the extension will be pulled from the resource object.
When uploading images to something like an AWS bucket, you may want to display the images. You may use a combination of the asset helper and setting a path in your filesystem config. This mainly just provides a nice interface for combining 2 strings
When using Amazon S3, you will need to set your bucket permissions and policies appropriately.
First, set a path in your filesystem config:
Then in your templates you can use the asset helper:
The signature is:
You may also specify multiple paths as a dictionary:
Then inside your asset helper you can use dot notation to specify the path you want to use:
Masonite has a simple yet powerful notification feature which is used to send notifications from your application. Here is a brief overview of what you can do with notifications:
Send E-mail
, Slack
and SMS
notifications
Store notifications in a database so they may be displayed in your web interface.
Queue notifications
Broadcast notifications
To create and send a notification with Masonite, you must first build a Notification
class. This class will act as the settings for your notification such as the delivery channels and the content of the notification for those different channels (mail, slack, sms, ...).
The first step of building a notification is running the command:
This will create your notification and it will look something like this:
Each notification class has a via
method that specify on which channels the notification will be delivered. Notifications may be sent on the mail
, database
, broadcast
, slack
and vonage
channels. More details on this later. When sending the notification it will be automatically sent to each channel.
If you would like to use an other delivery channel, feel free to check if a community driver has been developed for it or create your own driver and share it with the community !
via
method should returns a list of the channels you want your notification to be delivered on. This method receives a notifiable
instance.
You can send your notification inside your controller easily by using the Notification
class:
If the notification needs to be delivered to multiple channels you can chain the different routes:
database
channel cannot be used with those notifications because no Notifiable entity is attached to it.
If you want to send notifications e.g. to your users inside your application, you can define them as notifiables. Then you can still use Notification
class to send notification:
Or you can use a handy notify()
method:
ORM Models can be defined as Notifiable
to allow notifications to be sent to them. The most common use case is to set User
model as Notifiable
as we often need to send notifications to users.
To set a Model as Notifiable, just add the Notifiable
mixin to it:
You can now send notifications to it with user.notify()
method.
Then you can define how notifications should be routed for the different channels. This is always done by defining a route_notification_for_{driver}
method.
For example, with mail
driver you can define:
This is actually the default behaviour of the mail driver so you won't have to write that but you can customize it to your needs if your User model don't have email
field or if you want to add some logic to get the recipient email.
If you would like to queue the notification then you just need to inherit the Queueable
class and it will automatically send your notifications into the queue to be processed later. This is a great way to speed up your application:
You should define a to_mail
method on the notification class to specify how to build the email notification content.
The notification will be sent using the default mail driver defined in config/mail.py
. For more information about options to build mail notifications, please check out Mailable options.
If you want to override the mail driver for a given notification you can do:
You should define a to_slack
method on the notification class to specify how to build the slack notification content.
SlackComponent
takes different options to configure your notification:
Notifications can be sent to a Slack workspace in two ways in Masonite:
Incoming Webhooks
You will need to configure an "Incoming Webhook" integration for your Slack workspace. This integration will provide you with a URL you may use when routing Slack notifications. This URL will target a specific Slack channel.
Web API
You will need to generate a token to interact with your Slack workspace.
This token should have at minimum the channels:read
, chat:write:bot
, chat:write:user
and files:write:user
permission scopes. If your token does not have these scopes then parts of this feature will not work.
Then you can define this token globally in config/notifications.py
file as SLACK_TOKEN
environment variable. Or you can configure different tokens (with eventually different scopes) per notifications.
Slack notifications can use Slack Blocks Kit to build more complex notifications. Before using this you just have to install slackblocks
python API to handle Block Kit formatting.
Then you can import most of the blocks available in Slack documentation and start building your notification. You need to use the block()
option. Once again you can chain as many blocks as you want.
You can find all blocks name and options in slackblocks
documentation and more information in Slack blocks list.
Some blocks or elements might not be yet available in slackblocks
, but most of them should be there.
You should define the related route_notification_for_slack
method on your notifiable to return either
a webhook URL or a list of webhook URLs (if you're using Incoming Webhooks)
a channel name/ID or a list of channels names/IDs (if you're using Slack Web API)
To send a Slack notification without having a notifiable entity you must use the route
method
The second parameter can be a channel name
, a channel ID
or a webhook URL
.
When specifying channel names you must keep #
in the name as in the example. Based on this name a reverse lookup will be made to find the corresponding Slack channel ID. If you want to avoid this extra call, you can get the channel ID in your Slack workspace (right click on a Slack channel > Copy Name > the ID is at the end of url)
Sending SMS notifications in Masonite is powered by Vonage (formerly Nexmo). Before you can send notifications via Vonage, you need to install the vonage
Python client.
Then you should configure the VONAGE_KEY
and VONAGE_SECRET
credentials in notifications.py
configuration file:
You can also define (globally) sms_from
which is the phone number or name that your SMS will be sent from. You can generate a phone number for your application in the Vonage dashboard.
You should define a to_vonage
method on the notification class to specify how to build the sms notification content.
If the SMS notification contains unicode characters, you should call the unicode method when constructing the notification
The global sms_from
number can be overriden inside the notification class:
You should define the related route_notification_for_vonage
method on your notifiable to return a phone number or a list of phone numbers to send the notification to.
To send a SMS notification without having a notifiable entity you must use the route
method
Notifications can be stored in your application database when sent to Notifiable
entities. The notification is stored in a notifications
table. This table will contain information such as the notification type as well as a JSON data structure that describes the notification.
To store a notification in the database you should define a to_database
method on the notification class to specify how to build the notification content that will be persisted.
This method should return str
, dict
or JSON
data (as it will be saved into a TEXT
column in the notifications table). You also need to add database
channel to the via
method to enable database notification storage.
Before you can store notifications in database you must create the database notifications
table.
Then you can migrate your database
The ORM Model describing a Notification is DatabaseNotification
and has the following fields:
id
is the primary key of the model (defined with UUID4)
type
will store the notification type as a string (e.g. WelcomeNotification
)
read_at
is the timestamp indicating when notification has been read
data
is the serialized representation of to_database()
notifiable
is the relationship returning the Notifiable
entity a notification belongs to (e.g. User
)
created_at
, updated_at
timestamps
A notifiable entity has a notifications
relationship that will returns the notifications for the entity:
You can directly get the unread/read notifications:
You can mark a notification as read or unread with the following mark_as_read
and mark_as_unread
methods
Finally, keep in mind that database notifications can be used as any Masonite ORM
models, meaning you can for example make more complex queries to fetch notifications, directly on the model.
If you would like to broadcast the notification then you need to:
inherit the CanBroadcast
class and specify the broadcast_on
method
define a to_broadcast
method on the notification class to specify how to build the notification content that will be broadcasted
By default notifications will be broadcasted to channel(s) defined in broadcast_on
method but you can override this per notifiable by implementing route_notification_for_broadcast
method on your notifiable:
Masonite ships with a handful of notification channels, but you may want to write your own drivers to deliver notifications via other channels. Masonite makes this process simple.
Two methods need to be implemented in order to create a notification driver: send
and queue
.
get_data()
method will be available and will return the data defined in the to_voice()
method of the notification.
As any drivers it should be registered, through a custom provider for example:
Then you could scaffold this code into a new Masonite package so that community can use it 😉 !
You can enable dry notifications to avoid notifications to be sent. It can be useful in some cases (background task, production commands, development, testing...)
When fail_silently
parameter is enabled, notifications sending will not raise exceptions if an error occurs.
Channels defined in via()
method of the notification can be overriden at send:
Using channels
parameter we can send to other channels (if correctly defined in notification class):
Masonite ships with a powerful queue system. This feature is useful for running those tasks that take a while like sending emails, processing videos, creating invoices, updating records and anything else you don't need you users to wait for or jobs that need .
First, jobs are creating with the logic required to make the job run. The jobs are then "pushed" onto the queue where they will run later using "queue workers". You can specify as many queue workers as your server can run.
In addition to running jobs, some drivers allow you to monitor any jobs that fail. Job data will save into the database where they can be monitored and reran if needed.
You can easily modify the behavior of your queue system by changing the queue configuration:
The available queue drivers are: async
, database
and amqp
.
A full queue configuration will look like this:
The default key is used to specify which queue driver in the configuration to use by default. This needs to be the same value as one of the other keys in the configuration dictionary.
To use the database driver you should first create a jobs table:
This will create a migration file in your migrations directory.
If you want to save failed jobs you should also create a migration for the failed jobs table:
You should then migrate your project:
The database driver is used to process jobs and failed job via a database connection.
The AMQP driver is used for connection that use the AMQP protocol, such as RabbitMQ.
The available options include:
The async driver will simply run the jobs in memory using processes or threading. This is the simplest driver as it does not need any special software or setup.
The available options include:
In order to process things on the queue, you will need to create a job. This job will be treated as an entity that can be serialized and ran later.
For a shortcut you can run the job command to create the job:
You will now have a job class you can build out the logic for:
Any logic should be inside the handle method:
You can put jobs on the queue to process by simply passing them onto the queue:
You can also specify any number of options using keyword arguments on the push method:
To run a queue worker, which is a terminal process than runs the jobs, you can use the queue:work
command:
This will start up a worker using the default queue configurations. You can also modify the options:
A command with modified options will look like this:
If you configurations are setup properly, when jobs fail, they will go into a failed jobs table. This is where you can monitor why your jobs are failing and choose to rerun them or remove them.
If you choose to rerun your jobs, they will be placed back onto the queue at the end and rerun with the normal job queuing process.
To rerun jobs that failed you can use the command:
You can specify a few options as well:
Service Providers are the key building blocks to Masonite. The only thing they do is register things into the , or run logic on requests. If you look inside the config/providers.py
file, you will find a PROVIDERS
list which contains all the Service Providers involved in building the framework.
Although uncommon, You may create your own service provider and add it to your providers list to extend Masonite, or even remove some providers if you don't need their functionality. If you do create your own Service Provider, consider making it available on PyPi so others can install it into their framework.
We can create a Service Provider by simply using a craft command:
This will create a new Service Provider under our app/providers/DashboardProvider.py
. This new Service Provider will have two simple methods, a register
method and a boot
method. We'll explain both in detail.
There are a few architectural examples we will walk through to get you familiar with how Service Providers work under the hood. Let's look at a simple provider and walk through it.
We can see that we have a simple provider that registers the User
model into the container. There are three key features we have to go into detail here.
In our register
method, it's important that we only bind things into the container. When the provider is first registered to the container, the register method is ran and your classes will be registered to the container.
The boot method will have access to everything that is registered in the container. The boot method is ran during requests and is actually resolved by the container. Because of this, we can actually rewrite our provider above as this:
This will be exactly the same as above. Notice that the
boot
method is resolved by the container.
Once you create your own service provider, it will need to be registered in the PROVIDERS
list. This is likely in your config/providers.py
file:
Once registered it will take your register and boot method into account when the framework boots up or registers.
CORS is sometimes important to activate in your application. CORS allows you to have a stronger layer of security over your application by specifying specific CORS headers in your responses. This is done through "preflight" requests.
These "preflight" requests are OPTIONS requests that are sent at the same time as other non safe requests (POST, PUT, DELETE) and specifically designed to verify the CORS headers before allowing the other request to go through.
To learn more about CORS please read .
You can enable CORS protection by simply adding the CorsMiddleware into you middleware stack.
To enable CORS in Masonite, you just need to add the CorsMiddleware in your middleware stack to your http_middleware
key in your Kernel.py
class.
It is best to put the CORS middleware as the first middleware to prevent possible errors.
Your application will now handle CORS requests to successfully go through.
All CORS settings are configured in the dedicated security configuration file config/security.py
. The default configuration options are:
You can define paths for which you want CORS protection to be enabled. Multiple paths can be defined in the list and wildcards (*) can be used to define the paths.
Here all requests made to auth/
and to all API routes will be protected with CORS.
The default is to protect all HTTP methods but a list of methods can be specified instead. This will set the Access-Control-Allow-Methods
header in the response.
For example CORS can be enabled for sensitive requests:
The default is to allow all origins to access a resource on the server. Instead you can define a list of origins allowed to access the resources defined above (paths). Wildcards (*) can be used. This will set the Access-Control-Allow-Origin
header in the response.
Here blog.example.com
and forum.example.com
will e.g. be authorized to make requests to the application paths defined above.
The default is to authorized all request headers during a CORS request, but you can define a list of headers confirming that these are permitted headers to be used with the actual request. This will set the Access-Control-Allow-Headers
header in the response.
The default is an empty list but you can define which headers will be accessible to the broswser e.g. with Javascript (with getResponseHeader()
). This will set the Access-Control-Expose-Headers
header in the response.
This will set the Access-Control-Max-Age
header in the response and will indicates how long the results of a preflight request can be cached. The default is None
meaning it preflight request results will never be cached.
You can indicate a cache duration in seconds:
This will set the Access-Control-Allow-Credentials
and will indicate whether or not the response to the request can be exposed when the credentials
flag is true. The default is False
.
Masonite ships with an incredibly simple way to run recurring tasks. Tasks could be as simple as cleaning records on a database table every minute, or syncing records between databases, or sending invoices out at the end of the month. They are those automated recurring tasks you need to run on a schedule, like every minute, every hour, every day, every month, or anywhere in between.
Tasks are what you use to register to the Masonite scheduler do it knows which tasks to run and how often.
To create a task, simply run the command:
This will create a task that looks like this:
You can change the task to do whatever you need it to do:
Tasks will run as often as you specify them to run using the time options.
You can specify the tasks to run as often as you need them to. Available options are:
To run all the tasks that are registered, we can find and execute the ones that should run depending on the time of the computer / server:
To run only a specific registered task we can use --task
option:
Finally we can force task(s) to run immediately we can use --force
option. This can be especially useful when developing locally.
Setting up Cron Jobs are for UNIX based machines like Mac and Linux only. Windows has a similar schedule called Task Scheduler which is similar but will require different instructions in setting it up.
Although the command to schedule:run
above is useful and needed, we still need a way to run it every minute. We can do this with a cronjob on the server
We'll show you an example cron job and then we will walk through how to build it.
When a cron job runs, it will typically run commands with a /bin/sh command instead of the usual /bin/bash. Because of this, craft may not be found on the machine so we need to tell the cron job the PATH that should be loaded in. We can simply find the PATH by going to our project directory and running:
Which will show an output of something like:
If you are using a virtual environment for development purposes then you need to run the
env
command inside your virtual environment.
We can then copy the PATH and put it in the cron job.
To enter into cron, just run:
and paste the PATH
we just copied. Once we do that our cron should look like:
Exit out of nano. Now we just need to setup the actual cron job:
Now we just need to setup the cron task itself. This could be as easy as copying it and pasting it into the nano editor again. You may need to change a few things though.
The first * * * * *
part is a must and bascially means "run this every minute by default". The next part is the location of your application which is dependant on where you installed your Masonite application.
The next part is dependant on your setup. If you have a virtual environment then you need to activate it by appending && source venv/bin/activate
to the cron task. If you are not running in a virtual environment then you can leave that part out.
Lastly we need to run the schedule command so we can append && craft schedule:run
Great! Now we have a cron job that will run the craft command every minute. Masonite will decide which classes need to be executed.
There are a lot of times when you need to validate incoming input either from a form or from an incoming json request. It is wise to have some form of backend validation as it will allow you to build more secure applications. Masonite provides an extremely flexible and fluent way to validate this data.
Validations are based on rules where you can pass in a key or a list of keys to the rule. The validation will then use all the rules and apply them to the dictionary to validate.
You can see a .
Incoming form or JSON data can be validated very simply. All you need to do is import the Validator
class, resolve it, and use the necessary rule methods.
This whole snippet will look like this in your controller method:
This validation will read like "user and email are required and the terms must be accepted" (more on available rules and what they mean in a bit)
Note you can either pass in a single value or a list of values
You can simply display validation errors in views like this:
Sometimes you may cross a time where you need to create a new rule that isn't available in Masonite or there is such a niche use case that you need to build a rule for.
In this case you can create a new rule.
You can easily create a new rule boiler plate by running:
There is no particular reason that rules are lowercase class names. The main reason is that it improves readability when you end up using it as a method if you choose to register the rule with the validation class like you will see below.
This will create a boiler plate rule inside app/rules/equals_masonite.py that looks like:
Our rule class needs 3 methods that you see when you run the rule command, a passes
, message
and negated_message
methods.
Passes Method
The passes method needs to return some kind of boolean value for the use case in which this rule passes.
For example if you need to make a rule that a value always equals Masonite then you can make the method look like this:
When validating a dictionary like this:
then
the attribute
will be the value (Masonite
)
the key
will be the dictionary key (name
)
the dictionary
will be the full dictionary in case you need to do any additional checks.
Message method
The message method needs to return a string used as the error message. If you are making the rule above then our rule may so far look something like:
Negated Message
The negated message method needs to return a message when this rule is negated. This will basically be a negated statement of the message
method:
Now the rule is created we can use it in 1 of 2 ways.
Importing our rule
We can either import directly into our controller method:
or we can register our rule and use it with the Validator class as normal.
Register the rule
In any service provider's boot method (preferably a provider where wsgi=False
to prevent it from running on every request) we can register our rule with the validator class.
If you don't have a provider yet we can make one specifically for adding custom rules:
Then inside this rule provider's boot method we can resolve and register our rule. This will look like:
Now instead of importing the rule we can just use it as normal:
notice we called the method as if it was apart of the validator class this whole time.
Registering rules is especially useful when creating packages for Masonite that contain new rules.
In addition to validating the request class we can also use the validator class directly. This is useful if you need to validate your own dictionary:
Just put the dictionary as the first argument and then each rule being its own argument.
Rule enclosures are self contained classes with rules. You can use these to help reuse your validation logic. For example if you see you are using the same rules often you can use an enclosure to always keep them together and reuse them throughout your code base.
You can create a rule enclosure by running:
You will then see a file generated like this inside app/rules:
You can then fill the list with rules:
You can then use the rule enclosure like this:
You can also use this in addition to other rules:
Working with errors may be a lot especially if you have a lot of errors which results in quite a big dictionary to work with.
Because of this, Masonite Validation comes with a MessageBag
class which you can use to wrap your errors in. This will look like this:
You can easily get all errors using the all()
method:
This is just the opposite of the any()
method.
You can also merge an existing dictionary into the bag with the errors:
Sometimes you will need to check values that aren't on the top level of a dictionary like the examples shown here. In this case we can use dot notation to validate deeper dictionaries:
notice the dot notation here. Each .
being a deeper level to the dictionary.
Sometimes your validations will have lists and you will need to ensure that each element in the list validates. For example you want to make sure that a user passes in a list of names and ID's.
For this you can use the * asterisk to validate these:
Here is an example to make sure that street is a required field:
All errors returned will be very generic. Most times you will need to specify some custom error that is more tailored to your user base.
Each rule has a messages keyword arg that can be used to specify your custom errors.
Now instead of returning the generic errors, the error message returned will be the one you supplied.
Leaving out a message will result in the generic one still being returned for that value.
By default, Masonite will not throw exceptions when it encounters failed validations. You can force Masonite to raise a ValueError
when it hits a failed validation:
Now if the required rule fails it will throw a ValueError
. You can catch the message like so:
You can also specify which exceptions should be thrown with which key being checked by using a dictionary:
All other rules within an explicit exception error will throw the ValueError
.
In addition to using the methods provided below, you can also use each one as a pipe delimitted string. For example these two validations are identical:
These rules are identical so use whichever feels more comfortable.
The accepted rule is most useful when seeing if a checkbox has been checked. When a checkbox is submitted it usually has the value of on
so this rule will check to make sure the value is either on, 1, or yes.
This is used to verify that the domain being passed in is a DNS resolvable domain name. You can also do this for email addresses as well. The preferred search is domain.com but Masonite will strip out http://
, https://
and www
automatically for you.
Used to make sure the date is a date after today. In this example, this will work for any day that is 2019-10-21 or later.
You may also pass in a timezone for this rule:
Used to make sure the date is a date before today. In this example, this will work for any day that is 2019-10-19 or earlier.
You may also pass in a timezone for this rule:
This rule is used to make sure a key is "confirmed". This is simply a key_confirmation
representation of the key.
For example, if you need to confirm a password
you would set the password confirmation to password_confirmation
.
This is used to make sure a value exists inside an iterable (like a list or string). You may want to check if the string contains the value Masonite for example:
Used to check that an array value contains distinct items.
Used for running a set of rules when a set of rules does not match. Has a then()
method as well. Can be seen as the opposite of when.
This is useful for verifying that a value is a valid email address
Used to make sure a dictionary value is equal to a specific value
Checks to see if a key exists in the dictionary.
This is good when used with the when rule:
Used to make sure that value is a valid file.
Additionally you can check file size, with different file size formats:
Finally file type can be checked through a MIME types list:
You can combine all those file checks at once:
This is used to make sure a value is greater than a specific value
Used to make sure that value is a valid image.
Valid image types are defined by all MIME types starting with image/
. For more details you can check mimetypes
Python package which gives known MIME types with mimetypes.types_map
.
Additionally you can check image size as with basic file validator
Used when you need to check if an integer is within a given range of numbers
You can also check if the input is a valid IPv4 address:
Checks to see the date and time passed is in the future. This will pass even if the datetime is 5 minutes in the future.
You may also pass in a timezone for this rule:
Used to make sure the value is a list (a Python list instance)
*
notation can also be used
Used to make sure if a value is in a specific value
notice how 5 is in the list
Checks to see the date and time passed is in the past. This will pass even if the datetime is 5 minutes in the past.
You may also pass in a timezone for this rule:
This will negate all rules. So if you need to get the opposite of any of these rules you will add them as rules inside this rule.
For example to get the opposite if is_in
you will do:
This will produce an error because age it is looking to make sure age is not in the list now.
Used to make sure a given value is actually a JSON object
Used to make sure a string is of a certain length
This is used to make sure a value is less than a specific value
Used to make sure the value matches another field value
Used to make sure the value is None
Used to make sure a value is a numeric value
Sometimes you will want only one of several fields to be required. At least one of them need to be required.
This will pass because at least 1 value has been found: user
.
You can also use the phone validator to validate the most common phone number formats:
The available patterns are:
123-456-7890
(123)456-7890
Every country has their own postal code formats. We added regular expressions for over 130 countries which you can specify by using a comma separated string of country codes:
Please look up the "alpha-2 code" for available country formats.
Sometimes you want to do more complex validations on some fields. This rule allows to validate against a regular expression directly. In the following example we check that username
value is a valid user name (without special characters and between 3 and 16 characters).
Used to make sure that value is present and not empty only if an other field has a given value.
Used to make sure that value is present and not empty onlyf if any of the other specified fields are present.
Used to make sure the value is a string
The strong rule is used to make sure a string has a certain amount of characters required to be considered a "strong" string.
This is really useful for passwords when you want to make sure a password has at least 8 characters, have at least 2 uppercase letters and at least 2 special characters.
You can also validate that a value passed in a valid timezone
Used to make sure a value is a truthy value. This is anything that would pass in a simple if statement.
Used to make sure that value is a valid video file.
Valid video types are defined by all MIME types starting with video/
. For more details you can check mimetypes
Python package which gives known MIME types with mimetypes.types_map
.
Additionally you can check video size as with basic file validator
Conditional rules. This is used when you want to run a specific set of rules only if a first set of rules succeeds.
For example if you want to make terms be accepted ONLY if the user is under 18
Masonite includes a rate limiting feature which make it really easy to limit an action during a given time window.
The most frequent use of this feature is to add throttling to some API endpoints but you could also use this to limit how many time a model can be updated, a job can be run in the queue or a mail should be sent.
This feature is based on the application's features. It will store the number of attempts of a given key
and will also store the associated time window.
Rate Limiting feature can be accessed via the container application.make("rate")
or by using the RateLimiter
facade.
To limit an action, we need:
a key
that will uniquely identify the action
a number of authorized attempts
a delay after which number of attempts will be reset
Before we can start using the rate limiting features of Masonite, we need to register the RateProvider
class in our providers list:
You can add throttling to HTTP requests easily by using the ThrottleRequestsMiddleware
.
First register the middleware as a route middleware into your project:
This middleware is taking one argument which can be either a limit string or a limiter name.
By using limit strings such as 100/day
you will be able to add a global limit which does not link this limit to a given user, view or IP address. It's really an absolute limit that you will define on your HTTP requests.
Units available are: minute
, hour
and day
.
We now just have to use it in our routes:
To be able to add throttling per users or to implement more complex logic you should use Limiter
. Limiter
are simple classes with a allow(request)
method that will be called each time a throttled HTTP request is made.
Some handy limiters are bundled into Masonite:
GlobalLimiter
UnlimitedLimiter
GuestsOnlyLimiter
But you can create your own limiter:
Here we are creating a limiter authorizing 2 requests/day for guests users, 10 requests/day for authenticated non-premium users and unlimited requests for premium users. Here we are using by(key)
to define how we should identify users. (TODO: explain more)
Finally you can register your limiter(s) in your application provider:
We now just have to use it in our routes:
Now when making unauthenticated requests to our /api
endpoints we will see some new headers in the response:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 4
After reaching the limit two headers are added in the response, X-Rate-Limit-Reset
which is the timestamp in seconds defining when rate limit will be reset and when api endpoint will be available again and Retry-After
which is the number of seconds in which rate limit will be reset:
X-Rate-Limit-Limit
: 5
X-Rate-Limit-Remaining
: 0
X-Rate-Limit-Reset
: 1646998321
Retry-After
: 500
A ThrottleRequestsException
exception is raised and a response with status code 429: Too Many Requests
and content Too many attempts
is returned.
The response can be customized to provide different status code, content and headers. It can be done by adding a get_response()
method to the limiter.
In the previous example it would look like this:
To use Rate Limiting feature we should define limits. A limit is a number of attempts during a given time frame. It could be 100 times per day or 5 times per hour. In Masonite limits are abstracted through the Limit
class.
Here is an overview of the different ways to create limits:
Then to associate a given key to this limit we can do:
This feature allows to simply limit calling a Python callable. Here we will limit the given callable to be called 3 times per hour for the key sam
.
Let's make one attempt:
Alternatively you can manually incrementing attempts:
We can get the number of attempts:
We can get the number of remaining attempts:
We can check if too many attempts have been made:
We can reset the number of attempts:
We can get the seconds in which will be able to attempt the action again:
We can get the UNIX timestamps in seconds when will be able to attempt the action again:
Here is a complete use case, that will determine if an email should be send to the given user:
The Service Container is an extremely powerful feature of Masonite and should be used to the fullest extent possible. It's important to understand the concepts of the Service Container. It's a simple concept but is a bit magical if you don't understand what's going on under the hood.
The Service Container is just a dictionary where classes are loaded into it by key-value pairs, and then can be retrieved by either the key or value through resolving objects. That's it.
Think of "resolving objects" as Masonite saying "what does your object need? Ok, I have them in this dictionary, let me get them for you."
The container holds all of the frameworks classes and features so adding features to Masonite only entails adding classes into the container to be used by the developer later on. This typically means "registering" these classes into the container (more about this later on).
This allows Masonite to be extremely modular.
There are a few objects that are resolved by the container by default. These include your controller methods (which are the most common and you have probably used them so far) driver and middleware constructors and any other classes that are specified in the documentation.
There are three methods that are important in interacting with the container: bind
, make
and resolve
In order to bind classes into the container, we will just need to use a simple bind
method on our app
container. In a service provider, that will look like:
This will load the key value pair in the providers
dictionary in the container. The dictionary after this call will look like:
The service container is available in the Request
object and can be retrieved by:
Sometimes you really don't care what the key is for the object you are binding. For example you may be binding a Markdown
class into the container but really don't care what the key binding is called. This is a great reason to use simple binding which will set the key as the object class:
In order to retrieve a class from the service container, we can simply use the make
method.
That's it! This is useful as an IOC container which you can load a single class into the container and use that class everywhere throughout your project.
You can bind singletons into the container. This will resolve the object at the time of binding. This will allow the same object to be used throughout the lifetime of the server.
You can also check if a key exists in the container by using the has
method:
You can also check if a key exists in the container by using the in
keyword.
You may want to collect specific kinds of objects from the container based on the key. For example we may want all objects that start with "Exception" and end with "Hook" or want all keys that end with "ExceptionHook" if we are building an exception handler.
We can easily collect all objects based on a key:
This will return a dictionary of all objects that are binded to the container that start with anything and end with "ExceptionHook" such as "SentryExceptionHook" or "AwesomeExceptionHook".
We can also do the opposite and collect everything that starts with a specific key:
This will collect all keys that start with "Sentry" such as "SentryWebhook" or "SentryExceptionHandler."
Lastly, we may want to collect things that start with "Sentry" and end with "Hook"
This will get keys like "SentryExceptionHook" and "SentryHandlerHook"
You can also collect all subclasses of an object. You may use this if you want to collect all instances of a specific class from the container:
This is the most useful part of the container. It is possible to retrieve objects from the container by simply passing them into the parameter list of any object. Certain aspects of Masonite are resolved such as controller methods, middleware and drivers.
For example, we can type hint that we want to get the Request
class and put it into our controller. All controller methods are resolved by the container.
In this example, before the show method is called, Masonite will look at the parameters and look inside the container for the Request object.
Masonite will know that you are trying to get the Request
class and will actually retrieve that class from the container. Masonite will search the container for a Request
class regardless of what the key is in the container, retrieve it, and inject it into the controller method. Effectively creating an IOC container with dependency injection. capabilities Think of this as a get by value instead of a get by key like the earlier example.
Pretty powerful stuff, eh?
Masonite will also resolve your custom, application-specific classes including those that you have not explicitly bound with app.bind()
.
Continuing with the example above, the following will work out of the box (assuming the relevant classes exist), without needing to bind the custom classes in the container:
Another powerful feature of the container is it can actually return instances of classes you annotate. For example, all Upload
drivers inherit from the UploadContract
which simply acts as an interface for all Upload
drivers. Many programming paradigms say that developers should code to an interface instead of an implementation so Masonite allows instances of classes to be returned for this specific use case.
Take this example:
Notice that we passed in a contract instead of the upload class. Masonite went into the container and fetched a class with the instance of the contract.
The service container can also be used outside of the flow of Masonite. Masonite takes in a function or class method, and resolves it's dependencies by finding them in the service container and injecting them for you.
Because of this, you can resolve any of your own classes or functions.
Remember not to call it and only reference the function. The Service Container needs to inject dependencies into the object so it requires a reference and not a callable.
This will fetch all of the parameters of randomFunction
and retrieve them from the service container. There probably won't be many times you'll have to resolve your own code but the option is there.
Sometimes you may wish to resolve your code in addition to passing in variables within the same parameter list. For example you may want to have 3 parameters like this:
You can resolve and pass parameter at the same time by adding them to the resolve()
method:
Masonite will go through each parameter list and resolve them, if it does not find the parameter it will pull it from the other parameters specified. These parameters can be in any order.
If you need to utilize a container outside the normal flow of Masonite like inside a command then you can import the container directly.
This would look something like:
Sometimes when you resolve an object or class, you want a different value to be returned.
We can pass a simple value as the second parameter to the swap
method which will be returned instead of the object being resolved. For example this is used currently when resolving the Mail
class like this:
but the class definition for the Mail
class here looks like this:
How does it know to resolve the smtp driver instead? It's because we added a container swap. Container swaps are simple, they take the object as the first parameter and either a value or a callable as the second.
For example we may want to mock the functionality above by doing something like this in the boot method of a Service Provider:
Notice that we specified which class should be returned whenever we resolve the Mail
class. In this case we want to resolve the default driver specified in the projects configurations.
Instead of directly passing in a value as the second parameter we can pass in a callable instead. The callable MUST take 2 parameters. The first parameter will be the annotation we are trying to resolve and the second will be the container itself. Here is an example of how the above would work with a callable:
Notice that the second parameter is a callable object. This means that it will be called whenever we try to resolve the Mail
class.
Remember: If the second parameter is a callable, it will be called. If it is a value, it will simply be returned instead of the resolving object.
Sometimes we might want to run some code when things happen inside our container. For example we might want to run some arbitrary function about we resolve the Request object from the container or we might want to bind some values to a View class anytime we bind a Response to the container. This is excellent for testing purposes if we want to bind a user object to the request whenever it is resolved.
We have three options: on_bind
, on_make
, on_resolve
. All we need for the first option is the key or object we want to bind the hook to, and the second option will be a function that takes two arguments. The first argument is the object in question and the second argument is the whole container.
The code might look something like this:
Notice that we create a function that accepts two values, the object we are dealing with and the container. Then whenever we run on_make
, the function is ran.
We can also bind to specific objects instead of keys:
This then calls the same attribute but anytime the Request
object itself is made from the container. Notice everything is the same except line 6 where we are using an object instead of a string.
We can do the same thing with the other options:
Masonite provides secure hashing for storing user passwords or other data. Bcrypt and Argon2 protocols can be used with Masonite (default is Bcrypt).
Hashing configuration is located at config/application.py
file. In this file, you can configure which protocol to use.
You can use the Hash
facade to easily hash a string (e.g. a password):
Note that you can return a hash as bytes with:
To check that a plain-text string corresponds to a given hash you can do:
You can determine if the work factor used by the hashing protocol has changed since the string was hashed using needs_rehash
:
You can change hashing protocol configuration on the fly for all Hash methods:
You can also change protocol on the fly:
Masonite testing is very simple. You can test very complex parts of your code with ease by just extending your class with a Masonite unit test class.
Although Masonite uses pytest
to run tests, Masonite's test suite is based on unittest
. So you will use unittest
syntax but run the tests with pytest
. Because of this, all syntax will be in camelCase instead of PEP 8 lower_case_with_underscores. Just know that all TestCase assertions used during testing is in camelCase form to maintain unittest standards.
When running tests, Masonite will automatically set the to testing
. You are free to define other testing environment configuration values as necessary.
You can create a .env.testing
file. Feel free to load any testing environment variables in here. By default they will not be commited. When pytest
runs it will additionally load and override any additional environment variables.
You can simply create a file starting with test_
and then creating a test class inheriting from masonite TestCase
class.
You can also directly use the command
to create tests/unit/test_some_feature.py
:
That's it! You're ready to start testing. Read on to learn how to build your test cases and run them.
You can run tests by calling
Or a specific test method
Finally you can re-run the last failed tests automatically
When you run a test class each test method of this test class will be ran following a specific life cycle.
Running the above test class will create this output:
Note that tests methods are not always ran in the order specified in the class. Anyway you should not make the assumptions that tests will be run in a given order. You should try to make your tests idempotent.
All methods that begin with assert
can be chained together to run through many assertions. All other method will return some kind of boolean or value which you can use to do your own assertions.
Sometimes you need to assert that a given piece of code will raise a given exception. To do this you can use the standard assertRaises()
context manager:
Sometimes you need to test the output of a function that prints to the console. To do this in your tests you can use the captureOutput()
context manager:
Sometimes you would need to change the debug mode value during the lifetime of a test. To do this you can use the debugMode()
context manager:
During tests execution, print()
statements will not be visible. You can use the dump()
test helper to dump data to console during a test:
Note that you can provide a second argument to name the dump in console.
If you want to programmatically stop the test execution you can use the stop()
helper. You can even provide a reason.
Test are stopped by returning a pytest 2 exit code (user interruption).
Masonite comes with different helpers that can ease writing tests. Some of them have already been explained in sections above.
Enable exceptions handling during testing.
Disable exceptions handling during testing.
Note that exception handling is disabled by default during testing.
Enable CSRF protection during testing.
Disable CSRF protection during testing.
Note that CSRF protection is disabled by default during testing.
Add cookies that will be used in the next request. This method accepts a dictionary of name / value pairs. Cookies dict is reset between each test.
Add headers that will be used in the next request. This method accepts a dictionary of name / value pairs. Headers dict is reset between each test.
Set a given pendulum instance to be returned when a now
(or today
, tomorrow
yesterday
) instance is created. It's really useful during tests to check timestamps logic.
This allow to control which datetime will be returned to be able to always have an expected behaviour in the tests.
When using those helpers you should not forget to reset the default pendulum
behaviour with restoreTime()
helper to avoid breaking other tests. It can be done directly in the test or in a tearDown()
method.
Set the mocked time as tomorrow. (It's a shortcut to avoid doing self.fakeTime(pendulum.tomorrow())
).
Set the mocked time as yesterday.
Set the mocked time as an offset of a given unit of time in the future. Unit can be specified among pendulum units: seconds
, minutes
, hours
, days
(default), weeks
, months
, years
.
Set the mocked time as an offset of a given unit of time in the past. Unit can be specified among pendulum units: seconds
, minutes
, hours
, days
(default), weeks
, months
, years
.
Restore the mocked time behaviour to default behaviour of pendulum
. When using fake
time helpers you should not forget to call this helper at the end.
It can be done directly in the test or in a tearDown()
method.
You can test what has been output to standard console during Masonite unit tests thanks to useful console assertions.
Output here is the standard output often named stdout
. Error here is the standard error often named stderr
.
External packages, prints in your code can output content in console (as output or error).
If you want to assert content output by a Masonite command you should use assertions instead.
The following assertions are available:
Assert that nothing has been printed to the console.
Assert that something has been printed to the console (output or error).
Assert that console standard output is equal to given output.
Assert that console standard output contains given output.
Assert that console standard output does not contain the given output.
Assert that something has been output to console standard error.
Assert that console standard error is equal to given error.
Assert that console standard error contains given error.
By default, your tests are are not ran in isolation from a database point of view. It means that your local database will be modified any time you run your tests and won't be rollbacked at the end of the tests. While this behaviour might be fine in most case you can learn below how to configure your tests cases to reset the database after each test.
If you want to have a clean database for each test you must subclass the TestCase
class with DatabaseTransactions
class. Then all your tests will run inside a transaction so any data you create will only exist within the lifecycle of the test. Once the test completes, your database is rolled back to its previous state. This is a perfect way to prevent test data from clogging up your database.
Note that you can define the connection
that will be used during testing. This will allow you to select a different database that will be used for testing. Here is a standard exemple of database configuration file that you can use.
Masonite provides several database assertions that can be used during testing.
Assert that a table in the database contains the given number of records.
Assert that a table in the database contains records matching the given query.
Assert that a table in the database does not contain records matching the given query.
Assert that the given model instance has been deleted from the database.
You can test your running in console with craft
test helper.
This will programmatically run the command if it has been registered in your project and assert that no errors has been reported.
The following assertions are available when testing command with craft
.
Assert that command exited with code 0 meaning that it ran successfully.
Assert command output has errors.
Assert command output contains the given string.
Assert command output to be exactly the same as the given reference output. Be careful to add eventual line endings characters when using this assertion method.
Assert command output does not contain the given reference output.
Assert command output has exactly the given errors.
To make a request in your tests, you may use the get
, post
, put
, patch
, or delete
methods within your test. These methods do not actually issue a "real" HTTP request to your application. Instead of returning a Masonit class Response
instance, test request methods return a HTTPTestResponse
instance, which provides a variety of helpful assertions that allow you to inspect and assert application's responses.
The request and responses of a test can be fetch by accessing the request
and response
attributes.
During tests you can register routes used only for testing purposes. For this you can use the addRoutes()
method at beginning of your tests:
To check if a route exists, we can simple use either get
or post
:
You may use the withHeaders()
method to customize the request's headers before it is sent to the application. This method allows you to add any headers you would like to the request by providing them as a dict:
You may use the withCookies()
method to set cookie values before making a request. This method accepts a dictionary of name / value pairs:
If you want to make authenticated requests in your tests, you can use the actingAs()
method that takes a given User
record and authenticate him during the request.
The user will be persisted only during the lifetime of the test. Each request made during the test will be authenticated with the given user. If you need to logout the user in the test, you can use actingAsGuest()
method:
By default, all calls to your routes with the above methods will be without CSRF protection. The testing code will allow you to bypass that protection.
This is very useful since you don't need to worry about setting CSRF tokens on every request but you may want to enable this protection. You can do so by calling the withCsrf()
method on your test.
This will enable it on a specific test but you may want to enable it on all your tests. You can do this by adding the method to your setUp()
method:
Again you can disable this behaviour with withoutCsrf()
method.
As you have noticed, Masonite has exception handling which it uses to display useful information during development.
This is an issue during testing because we wan't to be able to see more useful testing related issues. Because of this, testing will disable Masonite's default exceptions handling and you will see more useful exceptions during testing. If you want to use Masonite's built in exceptions handling then you can enable it by running:
You can also disable exceptions handling again by using:
Masonite provides a variety of assertions methods to inspect and verify request/response logic when testing your application. Those assertions are available on the HTTPTestResponse
returned by get
, post
, put
, patch
, or delete
.
Assert that returned response contains the given content
string. Note that the response content will be eventually decoded if required.
Assert that returned response does not contain the given content
string. Note that the response content will be eventually decoded if required.
Assert that returned response contains in order the given strings. Note that the response content will be eventually decoded if required.
Assert that returned response has no content and the given HTTP status code. The default status code that is asserted is 204.
Assert that given route has the given name.
Assert that given route has not the given name.
Assert that the response has the given HTTP status code:
Assert that the response returns a 200 status code:
Assert that the response returns a 201 status code:
Assert that the response has as status code between 200 and 300
Assert that the response has as 401 status code
Assert that the response has as 403 status code
Assert that the response has as 500 status code
Assert that the response has the given header name and value (if given).
Assert that the response does not have the given header.
Assert the response has the given URI value in Location
header.
Assert that the response is a redirection to the given URI (if provided) or to the given route name with the given parameters (if provided).
Assert that the request contains the given cookie name and value (if provided).
Assert that the request contains the given unencrypted cookie name
Assert that the request contains the given cookie name and is expired.
Assert that the request does not contain the given cookie.
Assert that the session contains the given key and value (if provided).
Assert that the session does not contain the given key.
Assert that the session contains an errors
key or contains the given list of keys in errors
key.
Assert that the session does not contain an errors
key or that this key is empty.
Assert that the route returned the given view name.
Assert that view context contains a given data key and value (if provided).
Assert that view context contains exactly the given data keys. It can be a list of keys or a dictionary (here only keys will be verified).
Assert that given data key is not available in the view context.
Assert that a user is authenticated after the current request.
If a user instance is given it will assert that this user is authenticated:
Assert that a user is not authenticated after the current request.
Assert that the request has the given HTTP middleware. An HTTP middleware class should be provided.
Assert that the request has the given route middleware. The registration key of the middleware should be provided.
Assert that the route used the given controller. A class or a string can be provided. If it's a string it should be formatted as follow ControllerName@method
.
Assert that the route used has the given parameter name and value (if provided).
Assert that response is JSON and contains the given data dictionary (if provided). The assertion will pass even if it is not an exact match.
Assert that response is JSON and contains the given path, with eventually the given value if provided. The path can be a dotted path.
Assert that response is JSON and is strictly equal to the given dictionary.
Assert that response is JSON and has the given count of keys at root level or at the given key (if provided).
Assert that response is JSON and does not contain given path. The path can be a dotted path.
Handy dump
and dd
helpers are available on the HTTPTestResponse
returned by get
, post
, put
, patch
, or delete
during a unit test.
After getting a test response back from a request you can dump request headers in console by chaining this helper to the response:
After getting a test response back from a request you can dump response headers in console:
After getting a test response back from a request you can dump response and request headers in console and stop the test execution:
Here assertSessionHas
will not be executed as the test will be stopped before.
After getting a test response back from a request you can dump session data in console by chaining this helper to the response:
After getting a test response back from a request you can dump session data in console and stop the test execution:
Here assertSessionHas
will not be executed as the test will be stopped before.
Option | Description |
---|---|
Option | Description |
---|---|
Method | Description |
---|---|
Method | Description | Example |
---|---|---|
Option | Description |
---|---|
Option | Description |
---|---|
Option | Description |
---|---|
Option | Description |
---|---|
Option | Description |
---|---|
You can read more about it .
You must then register tasks to the Masonite scheduler. You can do so easily inside your app (if you don't have one, you should ):
Option | Description |
---|
If you want to handle errors in views specifically you will need to add the ShareErrorsInSessionMiddleware
middleware into your route middlewares. errors
will be injected to views as a instance allowing to handle errors easily:
This is used to verify that the value is a valid date. module is used to verify validity. It supports the RFC 3339 format, most ISO 8601 formats and some other common formats.
Used to check that value is different from another field value. It is the opposite of validation rule.
For image or video file type validation prefer the direct and validation rules.
Used to make sure the value is actually available in the dictionary and not null. This will add errors if the key is not present. To check only the presence of the value in the dictionary use .
Used to check that a value is a valid UUID. The UUID version (according to ) standard can optionally be verified (1,3,4 or 5). The default version 4.
This will automatically discover your tests following pytest . You can also run a specific test class
Assert that the given model instance has been from the database.
The can also be specified to authenticate the user with the given guard:
The can also be specified to check the authentication state on the given guard.
The can also be specified to check the authentication state on the given guard.
driver
The driver to use for this disk
path
The base path to use for this disk
driver
The driver to use for this disk
client
The Amazon S3 client key
secret
The Amazon S3 secret key
bucket
The Amazon S3 bucket name
path
A path to be used for displaying resources
exists(file_path)
Boolean to check if a file exists.
missing(file_path)
Boolean to check if a file does not exist.
stream
Creates a FileStream
object to manage a file stream.
copy('/file1.jpg', '/file2,jpg')
Copies a file from 1 directory to another directory
move('/file1.jpg', '/file2,jpg')
Moves a file from 1 directory to another
prepend('file.log', 'content')
Prepends content to a file
append('file.log', 'content')
Appends content to a file
put('file.log', 'content')
Puts content to a file
get('file.log')
Gets content of a file
put_file('directory', resource, "name")
Puts a file resource to a directory. Must be an instance of Masonite's UploadedFile
. Takes an optional third name
parameter to specify the file name
connection
Specifies the connection to use for finding the jobs and failed jobs table.
table
Specifies the name of the table to store jobs in
failed_jobs
Specifies the table to store failed_jobs in. Set to None
to not save failed jobs.
attempts
Specifies the default number of times to attempt a job before considering it a failed job.
poll
Specifies the time in seconds to wait before calling the database to find new jobs
tz
The timezone that the database should save and find timestamps in
username
The username of your AMQP connection
password
The password of your AMQP connection
port
The port of your AMQP connection
vhost
The name of your virtual host. Can get this through your AMQP connection dashboard
host
The IP address or host name of your connection.
channel
The channel to push the queue jobs onto
queue
The default name of the queue to push the jobs onto.
blocking
A boolean value on whether jobs should run synchronously. Useful for debugging purposes.
callback
The name of the method on the job that should run.
mode
Whether the queue should spawn processes or threads. Options are threading
or multiprocess
workers
The numbers of processes or threads that should spawn to run the jobs.
--driver database
Specifies which driver to use for this worker.
--queue invoices
Specifis which queue to use for this worker.
--connection mysql
Specifies the connection to use for the worker.
--poll 5
Specifies the time in seconds to wait to fetch new jobs. Default is 1 second.
--attempts 5
Specifies the number of attempts to retry a job before considering it a failed job. Default is 3 times.
--driver database
Specifies which driver to use to find the failed jobs.
--queue invoices
Specifis which queue to put the failed jobs back onto the queue with.
--connection mysql
Specifies the connection to use to fetch the failed jobs.
| Specifies this task to run every minute |
| Specifies this task to run every 15 minutes |
| Specifies this task to run every 30 minutes |
| Specifies this task to run every 45 minutes |
| Specifies this task to run every hour. |
| Specifies this task to run every day at midnight |
| Specifies this task to run every week on sunday at 00:00 |
| Specifies this task to run every first of the month at 00:00 |
| Specifies the time to run the job at. Can be used with other options like |
| Specifies the amount of time to run. Can be any combination of time like |
| Runs every day at the specified time. Time is in 24-hour time. |
| Runs at 8am and 5pm. |
When it comes to unit testing, you always want to test a unit of your piece of code. Your code might depends on third party services such as an API and you don't want to call it during your local tests or in your CI environment. That's when you should use mocking to mock the external parts or the part you don't want to test.
Masonite comes with some mocking abilities for some of the features relying on third party services. For other parts or you own code you can use Python mocking abilities provided by unittest.mock
standard module.
Masonite tests case have two helpers method fake()
and restore()
.
You can mock a Masonite feature by doing self.fake(feature)
and then restore it to the real feature behaviour by calling self.restore(feature)
. When a feature is mocked the real behaviour won't be called, instead a quick and simple implementation is ran, often offering the ability to inspect and test what happens.
Available features that can be mocked (for now) are:
When mocking emails it will prevent emails from being really sent. Typically, sending mail is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Masonite was instructed to send a given mailable.
Here is an example of how to mock emails sending in your tests:
Available assertions are:
seeEmailWasSent()
seeEmailWasNotSent()
seeEmailCountEquals(count)
seeEmailTo(string)
seeEmailFrom(string)
seeEmailReplyTo(string)
seeEmailBcc(string)
seeEmailCc(string)
seeEmailSubjectEquals(string)
seeEmailSubjectContains(string)
seeEmailSubjectDoesNotContain(string)
seeEmailContains(string)
seeEmailDoesNotContain(string)
seeEmailPriority(string)
When mocking notifications it will prevent notifications from being really sent. Typically, sending notification is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Masonite was instructed to send a given notification.
Here is an example of how to mock notifications sending in your tests:
Available assertions are:
assertNothingSent()
assertCount(count)
assertSentTo(notifiable, notification_class, callable_assert=None, count=None)
assertLast(callable_assert)
assertNotSentTo(notifiable, notification_class)
On Notifications instances:
assertSentVia(*drivers)
assertEqual(value, reference)
assertNotEqual(value, reference)
assertIn(value, container)
Available helpers are:
resetCount()
last()
For mocking any piece of code in Python you can use the standard unittest.mock
module. You can find more information in unittest documentation.
Here is basic example
For mocking external HTTP requests you can use the responses
module. You can find more information in responses documentation.
Masonite 1.5 is focused on a few bug fixes and changes to several core classes in order to expand on the support of third party package integration.
Masonite 1.5 is releasing also with a new official package for adding RESTful API's to your project. This package used API Resources which are classes that can add a plethora of capabilities to your API endpoints. You can read more about it at the Masonite Entry documentation.
Masonite 1.5 also adds additional support HTTP methods like PUT
, PATCH
and DELETE
. More information about these new routes can be found under the Routing documentation
Nearly all dependencies have been moved to the core Masonite package. The only thing inside the project that is installed using craft new
is the WSGI server (which is waitress by default) and Masonite itself. This will improve the ability to change and update dependencies.
HTML forms only support GET and POST so there is no the ability to add any other HTTP methods like PUT
and PATCH
which will change the actual request method on submission. You can read more about this in the Requests documentation.
You can now use functions for routes instead of the classes. These new functions are just wrappers around the classes itself. Read more about this under the Routing documentation.
In Masonite 1.4 and below was the Api()
route which added some very basic API endpoints. All references to API's have been removed from core and added to the new Masonite Entry official package.
If you would like to use API's you will have to use the Masonite Entry package instead.
You can now get and set any header information using the new Request.header
method. This also allows third party packages to manipulate header information. Read more about this in the Requests documentation.
You can now delete cookies using the delete_cookie
method as well as set expiration dates for them. See the Requests documentation for more information.
The Cache driver now has an update method which can update a cache value by key. This is useful if you want to change a key value or increment it. Storing a cache file also now auto creates that directory. Read more about this in the Caching documentation.
Craft commands have been built from the ground up with the cleo package. It's an excellent package that is built around the extendability of commands by using primarily classes (instead of decorator functions). Read more under The Craft Command documentation
It is now possible to add craft commands to craft. You can read more about how under The Craft Command documentation
You can now add more migration directories by adding it to the container with a key ending in MigrationDirectory
. This will add the directory to the list of directory that run when migrate commands are ran. You can read more about this in the Creating Packages documentation.
You can now add data to sessions using the new Sessions feature which comes with a memory and cookie driver for storing data.
In previous versions, Masonite has not been able to fetch the site packages directory if you were in a virtual environment because virtual environment directories are dynamically named depending on who created it. We have found a way to detect the virtual environment and the site packages directory so now there is no need to add the site packages directory manually to the packages configuration file
Masonite 1.3 comes with a plethora of improvements over previous versioning. This version brings new features such as Queue and Mail drivers as well as various bug fixes.
Previously when a you tried to redirect using the Request.redirect()
method, Masonite would sometimes send the browser to an infinite redirection. This was because masonite was not resetting the redirection attributes of the Request
class.
Previously the content length in the request header was not being set correctly which led to the gunicorn server showing a warning that the content length did not match the content of the output.
Previously the Request class simply got the input data on both POST
and GET
requests by converting the wsgi.input
WSGI parameter into a string and parsing. All POST input data is now retrieved using FieldStorage
which adds support for also getting files from multipart/formdata
requests.
You may now simply upload images to both disk and Amazon S3 storage right out of the box. With the new UploadProvider
service provider you can simply do something like:
As well as support for Amazon S3 by setting the DRIVER
to s3
.
These helper functions are added functions to the builtin Python functions which can be used by simply calling them as usual:
Notice how we never imported anything from the module or Service Container. See the Helper Functions documentation for a more exhaustive list
Very often you will want to have a single variable accessible in all of your views, such as the Request
object or other class. We can use the new View
class for this and put it in it's own service provider:
You can now specify anything that is in the container in your middleware constructor and it will be resolved automatically from the container
Specify the subdomain you want to target with your route. It's common to want to have separate routes for your public site and multi-tenant sites. This will now look something like:
Which will target test.example.com/dashboard
and not example.com/dashboard
. Read more about subdomains in the Routing documentation.
By default, masonite will look for routes in the app/http/controllers
namespace but you can change this for individual routes:
This will look for the controller in the thirdparty.routes
module.
Masonite now ships with a QueueManager
class which can be used to build queue drivers. Masonite ships with an async
driver which sends jobs to a background thread. These queues can process Jobs which ca be created with the new craft job
command. See the Queues and Jobs documentation for more information.
Masonite 2.1 introduces a few new changes that are designed to correct course for the 2.x family and ensure we can go far into the 2.x family without having to make huge breaking changes. It was questionable whether we should break from the 2.x family and start a new 3.x line. The biggest question was removing (actually disabling) the ability to resolve parameters and go with the more favorable annotation resolving. That could have made Masonite a 3.x line but we have ultimately decided to go with the 2.1 as a course correction. Below you will find all changes that went into making 2.1 awesome. Nearly all of these changes are breaking changes.
It is much easier to contribute to Masonite now since nearly classes have awesome docstrings explaining what they do, their dependencies and what they return.
We have completely removed parameter resolving. We can no longer resolve like this:
in favor of the more explicit:
This is a bit of a big change and will be most of the time spent on refactoring your application to upgrade to Masonite 2.1. If you already used the more explicit version then you won't have to worry about this change. It is still possible to resolve parameters by passing that keyword argument to your container to activate that feature:
This should help with upgrading from 2.0 to 2.1 until you have refactored your application. Then you should deactivate this keyword argument so you can be in line with future 2.x releases.
You can choose to keep it activated if that is how you want to create applications but it won't be officially supported by packages, future releases or in the documentation.
All middleware are now classes:
this is different from the previous string based middleware
Previously when getting an incoming JSON response, we had to get the values via the payload input like so:
which was kind of strange in hindsight. Now we can just straight up use the input:
Again this is only a change for incoming JSON responses. Normal form inputs remain the same.
Previously we had a facades module but it was being unused and we didn't see a future for this module so we moved the only class in this module to it's own class. All instances of:
now become:
We also noticed that for some reason we were parsing parameters before we found routes but we only ever needed those parameters inside our routes so we were parsing them whether we found a route or not. We moved the parsing of parameters into the if statement that executes when a route is found.
When we say "parsing route parameters" we mean the logic required to parse this:
into a usable form to use on the request class this:
This provider has been completely removed for the more recommended ResponseMiddleware which will need to be added to your HTTP middleware list:
We also noticed that for some reason we were parsing parameters before we found routes but we only ever needed those parameters inside our routes so we were parsing them whether we found a route or not. We moved the parsing of parameters into the if statement that executes when a route is found.
When we say "parsing route parameters" we mean the logic required to parse this:
into a usable form to use on the request class this:
This provider has been completely removed for the more recommended ResponseMiddleware which will need to be added to your HTTP middleware list:
We also noticed that for some reason we were parsing parameters before we found routes but we only ever needed those parameters inside our routes so we were parsing them whether we found a route or not. We moved the parsing of parameters into the if statement that executes when a route is found.
When we say "parsing route parameters" we mean the logic required to parse this:
into a usable form to use on the request class this:
You can now optionally use .
instead of /
in your views:
We moved the CSRF middleware completely into the core framework and allow developers to extend from it now. This will allow us to fix any security bugs that are apart of the CSRF feature.
You may see this pattern a lot in the future which is only extending classes from the core framework so we can hot fix things much better.
Masonite now has a plethora of docstrings on each and every class by default to really give the developer an understanding about what each default class is actually doing and what it is dependent on.
Masonite is also much more PEP 8 compliant. We removed all instances of triple single quotes: '''
for the more preferred and PEP 8 compliant double quotes """
for docstrings.
We also cleaned a lot of the classes generated by the auth command since those were pretty ugly.
We also removed all instances of helper functions by default since it was confusing developers and was throwing red squiggly marks for text editors. They are still available to be used but they will not be known to developers unless they discover them in the documentation. Now all default code explicitly resolves via the container and helper functions can be used on the developers own terms.
Helper functions are still available but you will need to use them on your own terms.
Now every application has a basic seeding structure setup which is the same as if running the craft seed
command. This is to promote more use of this awesome feature which can be used in migration files for quick seeding of databases for development.
We were previously not able to import code into our migration files or database seeders because the command line tool would not pick up our current working directory to import classes into. Now the migrations module and seeds module have 3 lines of code:
this helpers when running the command line to import code into these modules.
In development you would see a message like:
When you hit a route in development mode. Well you would also hit it in production mode too since that was never turned off. Although this is likely fine, it would slow down the framework significantly under load since it takes a bit of resources to print something that didn't need to be printed. This enables a bit of a performance boost.
This command gets the statuses of all migrations in all directories. To include third party migration directories that are added to your project.
simple
container bindingsSometimes you do not need to bind an object to any key, you just want the object in the container. For this you can now do simple
bindings like this:
This new mail helper can be used globally which points to the default mail driver:
|safe
filters on built in template helpers.We no longer need to do:
We can now simply do:
Previously we had to specify the status code as a string:
in order for these to be used properly. Now we can just specify the status code:
There is quite a bit of things to remember when binding various things into the container. For example when binding commands, the key needs to be postfixed with Command
like ModelCommand
. Now we can do things like:
Along with this there are several other methods to help you bind things into the container without having to remember all the special rules involved, if any.
We now have View Routes on all instances of the normal HTTP classes:
We previously used this method on the Cache class like so:
Now we removed the cache_
prefix and it is just:
We can now use the .without()
method on the request class which returns all inputs except the ones specified:
Previously the port was missing from the database configuration settings. This was fine when using the default connection but did not work unless added to the config.
Instead of doing something like:
We can now use a dictionary:
We can now specify a route with multiple HTTP methods. This can be done like so:
Core can now emit events that can be listened to through the container.
Now you can setup a way to send email verifications into your user signup workflow simply but inherting a class to your User model.
Now all redirections set the status code implicitly instead of explicitly needing to set them.
Now you can use craft middleware MiddlewareName
in order to scaffold middleware like other classes.
All views can optionally use dot notation instead of foward slashes:
is the same as:
We can now do container swapping which is swapping out a class when it is resolved. In other words we may want to change what objects are returned when certain objects are resolved. These objects do not have to be in the container in the first place.
You can now use a env
function to automatically type cast your environment variables turning a numeric into an int:
You can now resolve from a container with a parameter list in addition to custom parameters.
In addition to all the awesome things that craft auth
generates, we now generate password reset views and controllers as well for you
Fixed an issue where custom route compilers was not working well with request parameters
All new 2.1 projects have a seeder setup so you can quickly make some mock users to start off your application. All users have a randomly generated email and the password of "secret".
You can run seeders by running:
When setting headers we had to set the http_prefix to None more times then not. So it is set by default.
This:
can change to:
Originally the code:
would return None
if there was no header. Now this returns a blank string.
There is now an up and down command so you can put that in your application in a maintenance state via craft commands:
There is also a new MaintenanceModeMiddleware
:
We removed the store_prepend()
method on the upload drivers for the filename
keyword arg on the store method.
So this:
now becomes:
Masonite 1.6 brings mostly minor changes to the surface layer of Masonite. This release brings a lot of improvements to the engine of Masonite and sets up the framework for the release of 2.0.
Previously, all cookies were set with an HttpOnly flag when setting cookies. This change came after reading several articles about how cookies can be read from Javascript libraries, which is fine, unless those Javascript libraries have been compromised which could lead to a malicious hacker sending your domain name and session cookies to a third party. There is no the ability to turn the HttpOnly flag off when setting cookies by creating cookies like:
Because craft is it's own tool essentially and it needs to work across Masonite versions, all commands have been moved into the Masonite repository itself. Now each version of Masonite maintains it's own commands. The new craft version is 2.0
Before, you had to use the Manager class associated with a driver to switch a driver. For example:
Now you can switch drivers from the driver itself:
This version has been fine tuned for adding packages to Masonite. This version will come along with a new Masonite Billing package. The development of Masonite Billing has discovered some rough spots in package integrations. One of these rough spots were adding controllers that were not in the project. For example, Masonite Billing allows adding a controller that handles incoming Stripe webhooks. Although this was possible before this release, Masonite 1.6 has added a new syntax:
Notice the new forward slash in the beginning where the string controller goes.
Previously, controllers were created as they were specified. For example:
created a DashboardController. Now the "Controller" part of the controller is appended by default for you. Now we can just specify:
to create our DashboardController. You may was to actually just create a controller called Dashboard. We can do this by specifying a flag:
short for "exact"
It's also really good practice to create 1 controller per "action type." For example we might have a BlogController
and a PostController
. It's easy to not be sure what action should be in what controllers or what to name your actions. Now you can create a "Resource Controller" which will give you a list of actions such as show, store
, create
, update
etc etc. If what you want to do does not fit with an action you have then you may want to consider making another controller (such as an AuthorController
)
You can now create these Resource Controllers like:
Just like the global controllers, some packages may require you to add a view that is located in their package (like the new exception debug view in 1.6) so you may now add views in different namespaces:
This will get a template that is located in the masonite package itself.
You can now group routes based on a specific string prefix. This will now look like:
which will compile down into /dashboard/user
and /dashboard/user/1
The container was one of the first features coded in the 1.x release line. For Masonite 1.6 we have revisited how the container resolves objects. Before this release you had to put all annotation classes in the back of the parameter list:
If we put the annotation in the beginning it would have thrown an error because of how the container resolved.
Now we can put them in any order and the container will grab each one and resolve it.
This will now work when previously it did not.
The container will now resolve instances of classes as well. It's a common paradigm to "code to an interface and not an implementation." Because of this paradigm, Masonite comes with contracts that act as interfaces but in addition to this, we can also resolve instances of classes.
For example, all Upload drivers inherit the UploadContract contract so we can simply resolve the UploadContract which will return an Upload Driver:
Notice here that we annotated an UploadContract but got back the actual upload driver.
You can now search the container and "collect" objects from it by key using the new collect method:
which will find all keys in the container such as SentryExceptionHook and SentryWebHook and make a new dictionary out of them.
A complaint a few developers pointed out was that Masonite has too many dependencies. Masonite added Pusher, Ably and Boto3 packages by default which added a bit of overhead, especially if developers have no intentions on real time event broadcasting (which most applications probably won't). These dependencies have now been removed and will throw an exception if they are used without the required dependencies.
Masonite 1.6 + will slowly be rolling out various framework hooks. These hooks will allow developers and third party packages to integrate into various events that are fired throughout the framework. Currently there is the abilitity to tie into the exception hook which will call any objects loaded into the container whenever an exception is hit. This is great if you want to add things like Sentry into your project. Other hooks will be implemented such as View, Mail and Upload Hooks.
Masonite 1.4 brings several new features to Masonite. These features include caching, template caching, websocket support with Masonite calls Broadcasting and much more testing to make Masonite as stable as possible. If you would like to contribute to Masonite, please read the Contributing Guide and the How To Contribute documentation.
If you are upgrading from Masonite 1.3 then please read the Masonite 1.3 to 1.4 documentation.
We recognize that in order for frameworks to keep up with modern web application, they require real time broadcasting. Masonite 1.4 brings basic broadcasting of events to masonite and comes with two drivers out of the box: pusher
and ably
. If you'd like to create more drivers then you can do so easily by reading the About Drivers documentation. If you do create a driver, please consider making it available on PyPi so others can install it into their projects or open an issue on GitHub and make to add it to the built in drivers.
Masonite now has a built in caching class that you can use to either cache forever or cache for a specific amount of time.
Templates may have a lot of logic that are only updated every few minutes or even every few months. With template caching you can now cache your templates anywhere from every few seconds to every few years. This is an extremely powerful caching technique that will allow your servers to run less intensively and easily increase the performance of your application.
If a page gets hit 100 times every second then you can cache for 5, 10 or 15 seconds at a time to lessen the load on your server.
This feature only activates if you have the CacheProvider
loaded in your PROVIDERS
list. If you try to use these features without that provider then you will be hit with a RequiredContainerBindingNotFound
exception letting you know you are missing a required binding from a service provider. This provider comes out of the box in Masonite 1.4.
We have also updated the code to closely conform to PEP 8 standards.
Because of the caching features, we have added a bootstrap/cache
folder where all caching will be put but you can change this in the new config/cache.py
file.
Masonite 1.4 brings the idea of contracts which are very similar to interfaces in other languages. Contracts ensure that a driver or manager inherits has the same functionality across all classes of the same type.
Cross-Site Request Forgery is a crucial security milestone to hit and Masonite 1.4 brings that ability. With a new Service Provider and middleware, we can now add a simple {{ csrf_field }}
to our forms and ensure we are protected from CSRF attacks.
Managers were very redundant before this release so we made it much easier to create managers with 2 simple class attributes instead of the redundant method. Managers are used to manage features and drivers to Masonite.
Now the constructor of all middleware is resolved by the container. This means you may use the IOC dependency injection techniques like controller methods and drivers.
There were two unused imports in the models that Masonite created. These have been removed completely.
Masonite 2 brings an incredible new release to the Masonite family. This release brings a lot of new features to Masonite to include new status codes, database seeding, built in cron scheduling, controller constructor resolving, auto-reloading server, a few new internal ways that Masonite handles things, speed improvements to some code elements and so much more. We think developers will be extremely happy with this release.
Upgrading from Masonite 1.6 to Masonite 2.0 shouldn't take very long. On an average sized project, this upgrade should take around 30 minutes. We'll walk you through the changes you have to make to your current project and explain the reasoning behind it.
Checkout the Upgrade Guide for Masonite 1.6 to 2.0
Controller constructors are now resolved by the container so this removed some redundancy within your code and any duplicated auto resolving can now be directly in your constructor:
Read more in the Controllers documentation.
There is a new command that starts a Python shell and imports the container for you already. Test it out to verify that objects are loaded into your container correctly. It's a great debugging tool.
Read more in The Craft Command Introduction documentation.
Masonite 2 ships with an awesome little helper command that allows you to see all the routes in your application
Read more in The Craft Command Introduction documentation.
A huge update to Masonite is the new --reload
flag on the serve command. Now the server will automatically restart when it detects a file change. You can use the -r
flag as a shorthand:
Read more in The Craft Command Introduction documentation.
An incredible new feature is autoloading support. You can now list directories in the new AUTOLOAD
constant in your config/application.py
file and it will automatically load all classes into the container. This is great for loading command and models into the container when the server starts up.
You can also use this class as a standalone class in your own service providers.
Read more in Autoloading documentation.
Updated all libraries to the latest version with the exception of the Pendulum library which latest version is a breaking change and therefore was left out. The breaking change would not be worth it to add the complexity of upgrading so you may upgrade on a per project basis.
Previously you had to import classes like:
Now you can simply specify:
Because of this change we no longer need the same duplicated class names in the PROVIDERS list either.
Read more about changing duplicated class names under the Duplicate Class Names documentation.
Removed the need for the redirection provider completely. You need to remove this from your PROVIDERS
list.
Renamed Request.redirectTo
to Request.redirect_to
Also removed the .send() method and moved the dictionary into a parameter:
Read more in the Requests documentation.
Added a new Request.only method to fetch only specific inputs needed.
Read more in Requests documentation.
Added a new Request.get_request_method()
method to the Request
class.
Read more in Requests documentation.
You can now completely remove fetching of any inputs that Masonite handles internally such as __token and __method when fetching any inputs. This is also great for building third party libraries:
Read more in Requests documentation.
Because of the changes to internal framework variables, there are several changes to the CSRF middleware that comes in every application of Masonite.
Be sure to read the changes in the Upgrade Guide 1.6 to 2.0.
Added a new default package to Masonite that allows scheduling recurring tasks:
Read about Masonite Scheduler under the Task Scheduling documentation.
It's important during development that you have the ability to seed your database with dummy data. This will improve team development with Masonite to get everyones database setup accordingly.
Read more in the Database Seeding documentation.
Now all templates have a new static function in them to improve rendering of static assets
Read more in the Static Files documentation.
You can use the password helper to hash passwords more simply than using straight bcrypt:
Read more in the Encryption documentation.
You can now specify which location in your drivers you want to upload to using a new dot notation:
This will use the directory stored in:
Read more in the Uploading documentation.
Masonite 2 removes the bland error codes such as 404 and 500 errors and replaces them with a cleaner view. This also allows you to add custom error pages.
Read more in the Status Codes documentation.
Providers are now explicitly imported at the top of the file and added to your PROVIDERS list which is now located in config/providers.py
. This completely removes the need for string providers and boosts the performance of the application sustantially
Previously you had to append all routes with a /
character. This would look something like:
You can now optionally prefix this without a /
character:
Previously we had to do something like:
Now we can optionally get the parameter from the method definition:
Learn more in the Controllers documentation here.
This is used as a wrapper around I/O operations. It will also be a wrapper around the upload drivers and moving files around and other file management type operations
We can now specify directly in the configuration file whether or not the threading or multiprocessing for the async type operations.
Learn more in the Queues documentation here.
We added 4 new HTTP verbs: HEAD
, CONNECT
, OPTIONS
, TRACE
. You import these and use them like normal:
Learn more in the Routes documentation here.
If the incoming request is a JSON request, Masonite will now return all errors as JSON
This is more of an internal change for Core itself.
Before we had to specify that we wanted the server to auto-reload by specifying a -r flag:
Now we can just specify the serve command it will default to auto-reloading:
You can now specify it to NOT auto-reload by passing in 1 of these 2 commands:
Learn more in the Craft commands documentation here.
By default you can only upload image files because of security reasons but now you can disable that by doing an accept('*') option:
Learn more in the Uploading documentation here.
Learn more in the Views documentation here.
We moved from pytest to unittests for test structures.
Learn more in the Testing documentation here.
Added a new DatabaseTestcase
so we can properly setup and teardown our database. This works for sqlite databases by default to prevent your actual database from being destroyed.
Before in templates we had to specify a path to go back to but most of the time we wanted to go back to the current path.
Instead of:
We can now do:
In order to learn how to use this you can visit the documentation here.
Learn more in the Requests documentation here.
We built a new validation library from scratch and completely ripped out the old validation code. Any current validation code will need to be updated to the new way.
The new way is MUCH better. You can read about it in the new validation section here.
Learn more in the Validation documentation here.
Previously we needed to pass in the request object to the Auth class like this:
Now we have it a bit cleaner and you can just resolve it and the request class will be injected for you
You may not notice anything but now if you bind a class into the container like this:
It will be resolved when you resolve it:
This is why the Auth class no longer needs to accept the request class. Masonite will inject the request class for you when you resolve the class.
This works with all classes and even your custom classes to help manage your application dependencies
Auth
class.You can now do something like:
Learn more in the Authentication documentation here.
Previously, each route's regex was being compiled when Masonite checked for it but we realized this was redundant. So now all route compiling is done before the server starts.
This has given Masonite a bit of a speed boost.
Masonite now has the ability to remember the previous container bindings for each object. This can speed of resolving your code by 10-15x. This is disabled by default as it is still not clear what kind of issues this can cause.
This is scheduled to be set by default in the next major version of Masonite
Learn more in the Service Container documentation here.
with_errors()
method in order to cut down on setting an errors session.Now instead of doing this:
we can now shorten down the flashing of errors and do:
Learn more in the Requests documentation here.
Masonite 1.5 doesn't bring many file changes to Masonite so this upgrade is fairly straight forward and should take less than 10 minutes.
All requirements are now gone with the exception of the WSGI server (waitress
) and the Masonite dependency. You should remove all dependencies and only put:
If you have added your site packages directory to our packages configuration file, you can now remove this because Craft commands can now detect your site packages directory in your virtual environment.
Remove the masonite.providers.ApiProvider.ApiProvider
from the PROVIDERS
list as this has been removed completely in 1.5
If you are using the Api()
route inside routes/api.py
for API endpoints then remove this as well. You will need to implement API endpoints using the new Official Masonite Entry package instead.
You'll also have to add a new RESOURCES = []
line to your routes/api.py
file for the new Masonite Entry package if you choose to use it.
This release works with the new craft command release. Upgrade to version masonite-cli / 1.1+
. <1.1
will only work with Masonite 1.4 and below.
Simply run:
You may have to run sudo if you are using a UNIX machine.
Masonite 1.5 now has sessions that can be used to hold temporary data. It comes with the cookie and memory drivers. Memory stores all data in a class which is lost when the server restarts and the cookie driver sets cookies in the browser.
There is a new config/session.py
file you can copy and paste:
As well as add the SessionProvider
inside your PROVIDERS
list just below the AppProvider
:
That's it! You have officially upgrades to Masonite 1.5
The primary goal of Masonite was to restructure the Masonite internals so that I can make sure Masonite is around for the next 5-10 years. Masonite started as a learning project and went through version 1-4 in 4 years. In the very beginning, there really was no clear vision for the direction of the project so the foundation was not well thought out. It wasn't until version 2 that there was a clear vision and major structural changes. Version 1 looked incredibly different than version 3 yet they were built on the same foundation. You can see the issue .. There would be no way we would be able to get to version 6+ on the same foundation as version 1. Masonite needed a complete foundational rewrite. Meet Version 4.
Masonite originally started as a learning project in December of 2017. Over the years it has been a giant learning experience and Masonite has turned into something I never could have imagined. Because of the success that Masonite had, I found it was important to take a step back and make sure that the foundation of Masonite was built in a way that allowed Masonite to be as successful as it can be. Masonite 4 was written from complete scratch with everything I've learned over 4 years of maintaining the framework.
We will attempt here to go through everything new in Masonite 4. This documentation section will be a mix of both what is new and why it is new. Most of Masonite 4 changes are actually internal. Much of the interface and features of Masonite are identical to previous versions.
The foundation of Masonite has changed a lot. The IOC container, service providers and features are all the same but how they are registered and how features work together has changed.
As many of you are familiar, when you maintain any application for several years and you go through requirement changes and scope creap it leads to technical debt.
With Masonite, there were times where I would come into contact with a very large company that wanted to use Masonite that maybe didn't have a feature they needed and I would have to build the feature over the weekend or even during the night. I did this to try to capture the company to use Masonite. Many of those companies did go along with using Masonite but creating features this rapidly tends to lead to poor feature structure. Rapid development of features naturally tends to lead to high coupling of code. High coupling of code tends to lead to more difficult maintanance.
One of the goals of the Masonite 4 release is to have very very low coupled code. So one of the things I did with Masonite 4 was to make sure each feature's foundation was identical. So each feature has a main manager class which acts as the main interface for each feature and each feature is built around drivers. This makes expanding features simple. New service? New driver.
One of the downsides to Masonite development was that in the beginning, I had no clue what I was doing. Masonite 1 and Masonite 2 were completely different frameworks but they were built on the same foundation. See the issue?
One of the problems with this is that I thought it would be a great idea to simply make a project inside the Masonite core's code so I can more easily test certain features. The problem with this is that you can't use one of the PIP's most powerful features. Development mode. This is because the Masonite config directory would override the import inheritance. So the alternative with Masonite was to have to uninstall and install it everytime we made a change. This made developing with Masonite a lot longer and harder.
The tricky part is the only way to solve this issue is making everything in the project configurable. This is where the new Kernel file comes in. So previously we had controllers inside app/http/controllers
, middleware inside app/middleware
, etc.
Now with the Kernel file we register all of our paths in this file. We can then use those registrations to do what we need to do.
The project structure slightly changed. Masonite 4 gets rid of the app/http
directory as there was no real benefit to the directory. The http
directory was removed and we now just have app/controllers
and app/middleware
.
The resources/templates
directory has been removed. This has been replaced with a templates
directory.
Surpringly, the features in Masonite 4 look almost identical to Masonite 3. There are some new or improved features such as:
A new Notifications feature
Improved broadcasting feature to include a new features like private and presence channels as well as easy authentication support.
More powerful email support based around Mailable classes.
Several packages like Validation and task scheduling have been moved into the core repository from being separate packages.
Masonite 4 brings a new concept called facades. Facades are a simple proxy class that call a deeper implementation through Masonite's Service Container.
Here would be the same code, one using auto resolving and one using facades:
and using a facade:
You can see its a bit cleaner and also allows us to use these facades internally to reference other implementations without having code be too rigid.
Service providers have changed slightly. In Masonite 3, providers were:
Register each provider to the container
Then looped through to call the register method on each of them
If the WSGI attribute was false it would call the boot method on those providers
The server would then boot up
On a request the service providers where WSGI was true have their boot method ran in order
The new service provider flow looks like this:
Register each provider to the container
Provider register methods are called as each one registers to the container
The server will then boot up
On each request, all the boot methods are ran in order
The end goals are the same but the order the flow happens has been modified. This is largely an internal change.
.text()
The text you want to show in the message
.text('Welcome to Masonite!')
.to()
The channel you want to broadcast to. If the value you supply starts with a # sign then Notifications will make a POST request with your token to the Slack channel list API and get the channel ID. You can specify the channel ID directly if you don't want this behavior
.to('#general') .to('CHSUU862')
.send_from()
The username you want to show as the message sender. You can also specify either the url
or icon
that will be displayed as the sender.
.as_current_user()
This sets a boolean value to True on whether the message should show as the currently authenticated user.
.as_current_user()
.link_names()
This enables finding and linking channel names and usernames in message.
.link_names()
.can_reply()
This auhtorizes replying back to the message.
.can_reply()
.without_markdown()
This will not parse any markdown in the message. This is a boolean value and requires no parameters.
.without_markdown()
.unfurl_links()
This enable showing message attachments and links previews. Usually slack will show an icon of the website when posting a link. This enables that feature for the current message.
.unfurl_links()
.as_snippet()
Used to post the current message as a snippet instead of as a normal message. This option has 3 keyword arguments. The file_type
, name
, and title
. This uses a different API endpoint so some previous methods may not be used.
.as_snippet(file_type='python', name='snippet', title='Awesome Snippet')
.token()
Override the globally configured token.
.token('xoxp-359926262626-35...')
Masonite 2.3 brings a lot of code and quality of life improvements. There aren't a lot of major ground breaking changes and depending on how your application is structured, may not require much effort to upgrade.
Below is a list of changes that will be in Masonite 2.3:
Some larger changes include all packages for Masonite use SEMVER versioning while Masonite still using ROMVER as it has been since Masonite started.
Masonite will also not require any packages for you through Masonite requirements and will instead put the requirements in the requirements.txt
file in new projects. This will allow packages to release new Majors outside of the major upgrades of Masonite. So we can develop new package improvements faster.
The masonite.testing.TestSuite
class has been completely removed. This was a class that was obsolete and has been replace by the masonite.testing.TestCase
class anyway. The TestSuite
class was bascially wrappers around some of the commands that predated the newer testing features.
Recently there has been a good amount of talks about the limitations of compiling sass with the Python libsass
provider. Because of these limitations, Masonite 2.3 will remove it completely.
A Laravel Mix file will now come with new installed applications created by craft.
The Responsable class will be able to be inherited by any class and be returned in the controller. If the class has a get_response()
method then the class will be rendered using that method. The View
class now acts in this way.
Masonite has always supported the previous 4 Python versions. Since the release of Python 3.8 recently, Masonite will now only support Python 3.5, 3.6, 3.7 and 3.8. When Python 3.9 comes out, Python 3.5 will be dropped.
You can still use previous versions of Masonite if you need a previous supported Python version.
Masonite has moved away from the Auth
class and towards Guard
classes. These classes will be responsable for handling everything from getting the user to handling logging in and logging out.
There is also a much better way to register new guards so packages will be able to register their own guards with your app which you can then use or swap on the fly.
Auth
class for a Guard
classThis is a very small change but will save a lot of time when upgrading your application. Now anytime you resolve the Auth
class you will get an instance of a Guard
class instead. The use of both classes are identical so they should just work as is.
(This swap may be removed in Masonite 2.4+)
AuthenticationProvider
All the authentication stuff in the previous improvements have been abstracted to their own provider so you will need to a add a new provider to your providers list.
The Auth
class now contains a new method which returns a list of routes. This cleans up the web.py
file nicely when scaffolding.
The container can not longer have modules bound to it. These should instead be imported.
Added a few new assertion methods to help chaining methods and keeping tests short and fast. These include assertHasHeader
and assertNotHasHeader
.
Added a new download class to make it very easy to force downloads or display files.
This is used like this:
Forcing will make the file download and not forcing will display the file in the browser.
You can now run a craft preset
command which will generate some .json
files and example templates. There is a react
, vue
and bootstrap
preset currently.
Instead of a url like /?filter[name]=Joe&filter[user]=bob&email=user@email.com
parsing to:
It will now parse into:
Parsing the query to the original way is no longer possible. This also comes with a query_parse
helper which you can use to parse a query string the same way Masonite does.
The container has a helpful collect
method which allows you to collect all the classes in the container with a certain key or an instance of a class like this:
Will collect everything in the container where the binding key ends with Command
.
You can also collect everything that is a subclass:
This will collect everything that is a subclass of Responsable
This has now been expanded to also include instances of. So it will work for objects now and not just classes.
masonite/
Directory To The src/masonite
DirectoryThis is an internal change mostly and completely transparent to all who install Masonite. This allows installing third party packages into Masonite with the same namespace without namespace conflicts.
Masonite now handles the response as bytes. This allows for different classes to handle the response in different ways.
Previously Masonite ultimately converted everything to a string at the end but some things needed to be returned to the WSGI server as bytes (like the new file download feature). So if you need to handle the raw response then you will now expect bytes instead of a string.
The Scheduler has a few changes.
There are a few new methods on the tasks you can use like every
, every_minute
, every_15_minutes
, every_30_minutes
, every_45_minutes
, daily
, hourly
, weekly
, monthly
.
These can either be used inside the provider or inside the command to make a more expressive scheduling syntax.
Providers can now inherit the CanSchedule
class which gives it access to the new self.call()
method (which is used to schedule commands) and self.schedule()
method (which is used to schedule jobs).
These are really just helper methods to help bind things to the container more expressively.
You can now schedule jobs or commands in a new expressive way. In addition to setting the schedule interval as attributes on the Task you can now do it directly in the provider:
There are several other methods that will be documented on release.
We also changed the namespace from scheduler
to masonite.scheduler
. So you will need to refactor your imports.
There are now mailable classes which you can use to wrap some of the logic of building emails around. Instead of doing something like this:
You can now do:
Now you can simply return a tuple if you want to change the status code that gets returned.
For example before we had to do something like this:
Now you can simply return a tuple:
Previously we converted the response to a string when the request was finished but this prevented use cases where we wanted to return bytes (like returning an image or PDF). Now the conversion is happens (or doesn't happen) internally before the WSGI server needs to render a response. This results in a slight change in your application.
The CLI tool no longer needs to be installed as the first step. Now the first step would be to install masonite which will give you access to craft. From there you can create a new project.
Masonite API now uses the from masonite.api.
namespace rather than the previous from api.
Masonite Debugbar is a really helpful way to see the stats of your application while you are developing. You can use this information to help debug errors you are getting or even optimize your models and queries for speed and memory issues.
Masonite Debugbar also supports AJAX calls which you will be able to see directly in a dropdown on your toolbar.
Setting up Masonite Debugbar is simple.
First, install the package:
Put the provider at the end of your provider list:
Then publish the package:
Finally ensure debug mode is enabled else the debugbar will not be displayed !
Now when you go to a page in your application, you will see a debug bar at the bottom of the page.
The configuration file is created on collectors
Not all collectors may be equally important to you so you can set anyone of these to either True
or False
in order to enable or disable them in the debugbar.
Masonite Debugbar collects data to show in your debugbar as a tab. Each collector is 1 tab in the debugbar.
Below is a detailed explanation of each collector
The model collector shows how many models are hydrated on that request. Whenever you make a query call, a model instance has to be created to store that rows data. This could be a costly operation depending on how many rows are in the table you are calling.
The queries collector shows all the queries made during that request as well as the time it took to perform that query. Use this to see where bottle necks are in your application. Slow queries lead to slow load times for your users.
The request collector shows you information related to the request such as inputs, parameters and headers.
The message collector contains messages you can add in your application. Instead of adding a bunch of print statements you can add a message:
You could also add tags which will create a colored tag in the content tab:
This collector adds all of your environment variables to your debugbar as well as the Python and Masonite versions.
The measures collector you can use to measure 2 points in your application. By default there is the time it takes for your application to complete the whole request. You can start and stop any measures you want:
You will now see the time it takes to run this code in the measures tab
If you find a need to create your own collector, maybe to log information related to exceptions or something similar, you can create your own collector simply:
Collectors are simple instances like this:
The restart method is required to restart your collector have each request so the information doesn't get persisted bewteen requests. If this is not required for your collector then you can simply return self
.
The next part you'll need is a way to add data to your collector. This can be done in any method you want your developers to use. These are your external API's and how you want developers interacting with your collector. You can name these methods whatever you want and can be as complex as you need them to be.
This method could be as simple or as complex as you need. Some of Masonite Debugbar's collectors use special classes to keep all the information.
Next you need a collect
and an html
method to finalize the collector. First the collect method should just return a dictionary like this:
data
here in the html method loop is the collection we built. You can use this to style your tab and content.
In the collect method return dictionary, the description
is used to show a quick description at the top of the tabs content. The count
here is the number badge shown in the actual tab itself.
Lastly we just need to register the collector to the debugbar for it to show up. You can do this in your own provider. If you are building this in your own application you can make your own provider. If you are building a collector as part of a package you can have your developers install it in their projects.
And then finally register the provider with your provider config and your collector will now show up in the debug toolbar with the rest of the collectors.
Masonite 1.4 brings several new features and a few new files. This is a very simple upgrade and most of the changes were done in the pip package of Masonite. The upgrade from 1.3 to 1.4 should take less than 10 minutes
This requirement file has the masonite>=1.3,<=1.3.99
requirement. This should be changed to masonite>=1.4,<=1.4.99
. You should also run pip install --upgrade -r requirements.txt
to upgrade the Masonite pip package.
There is now a new cache folder under bootstrap/cache
which will be used to store any cached files you use with the caching feature. Simply create a new bootstrap/cache
folder and optionally put a .gitignore
file in it so your source control will pick it up.
Masonite 1.4 brings a new config/cache.py
and config/broadcast.py
files. These files can be found on the page and can be copied and pasted into your project. Take a look at the new file and the file. Just copy and paste those configuration files into your project.
Masonite comes with a lot of out of the box functionality and nearly all of it is optional but Masonite 1.4 ships with three new providers. Most Service Providers are not ran on every request and therefore does not add significant overhead to each request. To add these 3 new Service Providers simple add these to the bottom of the list of framework providers:
Note however that if you add the CsrfProvider
then you will also need the CSRF middleware which is new in Masonite 1.4. Read the section below to add the middleware
Masonite 1.4 adds CSRF protection. So anywhere there is any POST form request, you will need to add the {{ csrf_field }}
to it. For example:
Lastly, put that middleware into the HTTP_MIDDLEWARE
list inside config/middleware.py
like so:
should now be:
with this change
Masonite 2 brings an incredible new release to the Masonite family. This release brings a lot of new features to Masonite to include new status codes, database seeding, built in cron scheduling, controller constructor resolving, auto-reloading server, a few new internal ways that Masonite handles things, speed improvements to some code elements and so much more. We think developers will be extremely happy with this release.
Upgrading from Masonite 1.6 to Masonite 2.0 shouldn't take very long although it does have the largest amount of changes in a single release. On an average sized project, this upgrade should take around 30 minutes. We'll walk you through the changes you have to make to your current project and explain the reasoning behind it
Masonite 2 adds some improvements with imports. Previously we had to import providers and drivers like:
Because of this, all framework will need to cut out the redundant last part. The above code should be changed to:
Masonite 2 brings a more explicit way of declaring Service Providers in your application. You'll need to take your current PROVIDERS
list inside the config/application.py
file and move it into a new config/providers.py
file.
Now all Service Providers should be imported at top of the file and added to the list:
String providers will still work but it is not recommended and will not be supported in current and future releases of Masonite.
There are a few changes in the wsgi.py
file and the bootstrap/start.py
file.
In the wsgi.py
file we should add a new import at the top:
Then change the code logic of bootstrapping service providers from:
to:
and change the logic in bootstrap/start.py
to:
Notice here we split the providers list when the server first boots up into two lists which significantly lowers the overhead of each request.
This change should significantly boost speed performances as providers no longer have to be located via pydoc. You should see an immediate decrease in the time it takes for the application to serve a request. Rough time estimates say that this change should increase the request times by about 5x as fast.
Again, with the addition of the above change, any place you have a duplicated class name like:
You can change it to:
Renamed Request.redirectTo to Request.redirect_to. Be sure to change any of these instances accordingly.
All instances of:
should be changed to:
Also removed the .send()
method completely on the Request
class so all instances of:
Need to be changed to:
Some variable internals have changed to prepend a double underscore to them to better symbolize they are being handled internally. Because of this we need to change any instances of csrf_token to __token in the CSRF Middleware file.
Simply add a new AUTOLOAD
constant in your config/application.py
file. This is the entire section of the autoload configuration.
By default this points to the app directory where models are stored by default but if you moved your models to other directories like app/models or app/admin/models then add those directories to your list:
Because of a minor rewrite of the Request class, we now do not need the RedirectionProvider. You can remove the RedirectionProvider completely in your PROVIDERS
list.
There is a new status code provider which adds support for adding custom status codes and rendering better default status code pages such as 400 and 500 error pages. This should be added right above the StartResponseProvider:
The .env got a small upgrade and in order to make the APP_DEBUG
variable consistent, it should be set to either True
or False
. Previously this was set to something like true
or false
.
Masonite 2 also removed the APP_LOG_LEVEL
environment variable completely.
That's it! You're all done upgrading Masonite 1.6 to Masonite 2.0. Build something awesome!
Masonite 3 is a big change from Masonite 2. The most obvious change is the fact that we have dropped support for Orator and have created a new package called Masonite ORM that is intended to be a drop in replacement of Orator.
Hopefully many of you may not even tell we are no longer using Orator.
Below is a list of high level changes of things that are new or changed in Masonite 3 from Masonite 2. If you want to see how to upgrade from Masonite 2 to 3 then refer to the
Since Python 3.9 came out we are dropping support for Python 3.5. Masonite adds some f string formats so Masonite will break on Python 3.5. Throughout Masonite 3.0 we will be upgrading the codebase to use features available to us that we were unable to do because of Python 3.5 support.
Masonite 3 supports all Python versions 3.6 and above.
In Masonite 2 the headers were largely controlled oddly. Internally they were just saved as dictionaries and then attached to the response later. Also strangely, the response headers were attached to the request class and not the response class, even though they really had nothing to do with the response class. This also presented an issue because a request header is one sent by a client and a response header is one set by your app but they were both being saved in the same place so it was impossible to be able to tell who set the header.
Now in Masonite 3 we refactored the headers to use a new HeaderBag
class which is used to maintain Header
classes. We put the exact same class on the response class as well so they can be managed separately.
Now if you want to set headers on the request or response and you can know which header is for which.
Also logically it makes more sense to set response headers on the response class.
This internal rewrite also negates the need to prefix headers using HTTP_
.
Cookies were suffering the same fate as headers so we changed cookies to use the same class structure as the HeaderBag
and there is now a CookieJar
class that is used to maintain Cookie
classes.
The request class has also been reworked. Before, the request class was a single class that was created when the framework booted up. This presented some challenges because the class needed to be maintained between requests. Meaning certain inputs and headers set on the request class needed to be reset once the request was over and set back to a state before the request. This obviously created some weird caching bugs between requests and instead of fixing the issues we actually just created hacky work arounds like resetting inputs.
Now the request class is only created once a request is received. Because of this there are now certain places that the request class is no longer accessible. For example, you can no longer fetch the request class in the register method of service providers where the wsgi
attribute is False
. You mat also not be able to fetch the request class in some of your classes __init__
method depending on when and where the class is being initialized.
If you are upgrading to Masonite 3 and run across an error like the request class is not found in the container then you will need to fetch the request class later down the code.
The request class was one of the first classes that was created for Masonite. There hasen't been much that changed on the class so the class slowly got larger and larger and took on more responsibility.
One of the things that the class was used for, like the headers, was the response status code. It did not make sense to set the response status code on the request class so now the response status is whatever status is set on the response class. Requests don't have status codes so we removed the status code on the request class all together.
Every Masonite project has had a bootstrap/start.py file. This is a low level method in the masonite app that really handles the entire application and responsible for the final response. It is the entry point everytime a request is received. Many of you were probably not even aware it was there or were confused on what it did.
Since the method was low level and should never be changed we moved this method into the Masonite codebase and instead of an import from the local bootstrap.start import app
it is now an import from the masonite codebase from masonite.wsgi import response_handler
It is largely the same exact method but is now maintained by the masonite framework.
Masonite queueing had a simple table with not a lot of options on it. Because of this we had to make some decisions to prevent the duplication of some jobs. Like sleeping and only fetching 1 job per poll. This made the queue slower than what it should have been. So now we added more columns on the queue jobs table that will allow us to reserve some jobs to prevent duplication in other queue workers. If you still experience duplication of some jobs running via supervisor you may need to specify the option that offsets when supervisor commands are started.
In the past we had route helpers which were functions that wrapped some route logic. These were located in the masonite.helpers.routes
namespace. These were deprecated in Masonite 2.3 and now removed in Masonite 3.0.
Obviously the biggest change is dropping Orator and picking up Masonite ORM. This new ORM is designed to be a drop in replacement for Orator. I have upgraded several projects to use the new ORM and I had to change very minimal code to get it to work.
Theres a few resons we decided to drop support of Orator but the main one was that Sdispater (the creator of Orator) has his time occupied by other packages like Pendulum (which Masonite ORM still uses) as well as Poetry. These are great packages and more popular than Orator. Sdispater does not know if he will pick Orator back up but the issues and bugs have been piling up and the codebase was not up to my standard of being maintained. Myself and a few maintainers have taken the time to create a new ORM project called Masonite ORM.
Another reason is that we now have completely creative control over the ORM side of Masonite. We don't have to go through someone who has complete control. Releases can now be scheduled whenever we want and we can add whatever features we want. This is a huge deal for Masonite.
Masonite 2.1 is a fantastic release. It works out a lot of the kinks that were in 2.0 as well as brings several new syntactically good looking code generation
This guide just shows the major changes between version to get your application working on 2.1. You should see the documentation to upgrade smaller parts of your code that are likely to be smaller quality of life improvements.
For 2.1 you will need masonite-cli>=2.1.0
.
Make sure you run:
Middleware has been changed to classes so instead of doing this in your config/middleware.py
file:
You will now import it directly:
This is likely the biggest change in 2.0. Before 2.1 you were able to fetch by key when resolving by doing something like:
We have removed this by default and now you much explicitly import your classes in order to interact with the container resolving:
If you truly do not like this change you can modify your container on a per project basis by adding this to your container constructor in wsgi.py
:
Just know this is not recommended and Masonite may or may not remove this feature entirely at some point in the future.
Previously we were able to do something like this:
Since we never actually created a class from this and you were not able to explicitly resolve this, we utilized the new container swapping in order to swap a class out for this container binding.
All instances above should be changed to:
Don't forgot to also do your boot
methods on your Service Providers as well:
As well as all your middleware and custom code:
You may have classes you binded personally to the container like this:
To get this in line for 2.1 you will need to use Container Swapping in order to be able to resolve this. This is actually an awesome feature.
First go to your provider where you binded it to the container:
and add a container swap right below it by swapping it with a class:
now you can use that class to resolve:
Completely removed the masonite.facades
module and put the only class (the Auth
class) in the masonite.auth
module.
So all instances of:
need to be changed to:
The StartResponseProvider
was not doing anything crazy and it could be achieved with a simple middleware. This speeds up Masonite slightly by offsetting where the response preparing takes place.
Simply remove the StartResponseProvider
from your PROVIDERS
list:
As well as put the new middleware in the HTTP middleware
In 2.0 you had to fetch incoming JSON payloads like this:
So now all instances of the above can be used normally:
CSRF middleware now lives in core and allows you to override some methods or interact with the middleware with class attributes:
Replace your current CSRF Middleware with this new one:
If you made changes to the middleware to prevent middleware from being ran on every request you can now set that as a class attribute:
This also allows any security issues found with CSRF to be handled on all projects quickly instead of everyone having to patch their applications individually.
In migrations (and seeds) you will need to put this import inside a __init__.py
file in order to allow models to be imported into them
There was a slight change in the bootstrap/start.py file
around line 60
.
This line:
Needs to be changed to:
You no longer should bind directly to the Response
key in the container. You should use the new Response object.
All instances of:
should now be:
and any instance of:
should be changed to:
Restructuring some sample code would be changing this:
to this:
The Cache.cache_exists()
has been changed to just Cache.exists()
. You will need to make changes accordingly:
Although not a critical upgrade, it would be a good idea to replace all instances of retrieval of environment variables with the new masonite.env
function.
Change all instances of this:
with the new env
function:
What this will do is actually type cast accordingly. If you pass a numeric value it will cast it to an int and if you want a boolean if will cast True
, true
, False
, false
to booleans like this:
if you don't want to cast the value you can set the cast
parameter to False
We removed the store_prepend()
method on the upload drivers for the filename
keyword arg on the store method.
So this:
now becomes:
Not much has changed in the actual project structure of Masonite 1.6 so we just need to make some minor changes to our existing 1.5 project
We just have to change our Masonite dependency version in this file:
Masonite 1.6 now wraps the majority of the application in a try and catch block so we can add exception handling such as the new debug view and other exception features down the road such as Sentry and other error logging.
In the middle of the file (line 45 if you have not made changes to this file) we can simply wrap the application:
This will also display an exception view whenever an exception is it.
That's all the changes we have to make for our project.
Welcome to the upgrade guide to get your Masonite 2.1 application working with Masonite 2.2. We'll be focusing on all the breaking changes so we can get all your code working on a Masonite 2.2 release cycle.
We will not go into all the better ways to use some of the features. For those changes be sure to read the "" documentation to the left to see what fits into your application and what doesn't. We will only focus on the breaking changes here.
Masonite 2.2 is jam packed with amazing new features and most of which are backwards compatible so upgrading from Masonite 2.1 to 2.2 is really simple.
We'll go through each section that your application will need to be upgraded and how it can be done.
Each upgrade will have an impact rating from LOW to HIGH. The lower the rating, the less likely it will be that your specific application needs the upgrade.
First let's upgrade Masonite to 2.2 first so we can see any exceptions that will be raised.
Let's upgrade by doing:
You can also add it to your requirements.txt or Pipfile.
In Masonite 2.1, route helpers were deprecated and you likely started receiving deprecation warnings. In Masonite 2.2, these were removed. You may have had routes that looks like this:
You will now need to remove all these and use the class based ones. To make this easier we can just import the get and post helpers and alias them like this:
Impact: MEDIUM
Masonite 2.2 completely removes the validation library that shipped with Masonite in favor of a brand new one that was built specifically for Masonite.
You'll need to add a new validation provider if you want your application to have the new validation features.
Add it by importing it into config/providers.py
and add it to your PROVIDERS
list:
Masonite 2.2 completely removed the validation package from 2.1 and created an even better all new validation package. You'll have to remove all your validation classes and use the new validation package.
For example you may have had a validation class that looked like this:
and used it inside your views like this:
This is now completely changed to use a better and more sleeker validation. The above validation can now be written like this:
Masonite 2.2 changes a bit how the masonite.auth.Auth
class resolves out of the container and how it resolves its own dependencies.
Now instead of doing something like:
You'll need to move this into the parameter list so it can be resolved:
There should be quite a bit of these in your application if you have used this class or you have used the built in craft auth
scaffold command.
Impact: MEDIUM
The behavior for resolving classes has now been changed. If you bind a class into the container like this:
It previously would have resolved and gave back the class:
This will now resolve from the container when you resolve it as a parameter list. This means that you will never get back a class inside places like controllers.
Now the above code would look something like this:
notice it now returns an object. This is because Masonite will check before it resolves the class if the class itself needs to be resolved (if it is a class). If SomeClass
requires the request object, it will be passed automatically when you resolve it.
Masonite 2.2 focused a lot on new testing aspects of Masonite and has some big rewrites of the package internally.
The UnitTest class has been completely removed in favor of the new masonite.testing.TestCase
method.
An import that looked like this:
Should now look like this:
All classes have now been changed to unittest classes. This will still work with pytest and you can still run python -m pytest
. The only thing that changes is the structure of the setup_method()
. This has been renamed to setUp()
.
A class like this:
Should now look like this:
Previously all methods were snake_case
but to continue with the unittest convention, all testing methods are camelCase
.
A method like:
Now becomes:
Again this is to prevent developers from needing to switch between snake_case
and camelCase
when using Masonite methods and unittest methods.
The route method that looked something like this:
Has now been replaced with the method name of the route. So to get a GET route you would do:
or a POST route:
So be sure to update all methods of self.route()
with the correct request methods.
In 2.1 you had to manually load your routes in like this:
this is no longer required and routes will be found automatically. There is no longer a self.routes()
method.
The JSON method signature has changed and you now should specify the request method as the first parameter.
A previous call that looked like this:
should become:
Previously you logged a user in by using the user
method but now you can using the actingAs
method before you call the route:
A method like this:
Should now be:
Masonite tries to make static files extremely easy and comes with whitenoise out of the box. Whitenoise wraps the WSGI application and listens for certain URI requests that can be registered in your configuration files and serves those assets.
All configurations that are specific to static files can be found in config/filesystem.py
. In this file you'll find a constant file called STATICFILES
which is simply a dictionary of directories as keys and aliases as the value.
The directories to include as keys are simply the location of your static file locations as a relative path starting from the base of your application. For example, if your css files are in storage/assets/css
then put that folder location as the key. For the value, put the alias you want to use in your templates. For this example, we will use css/
as the alias.
For this setup, our STATICFILES
constant should look like:
Now in our templates we can use:
Which will get the storage/assets/css/style.css
file.
All templates have a static function that can be used to assist in getting locations of static files. You can specify the driver and locations you want using the driver name or dot notation.
Take this for example:
this will render:
You can also make the config location a dictionary and use dot notation:
and use the dot notation like so:
Sometimes you may need to serve files that are normally in the root of your application such as a robots.txt
or manifest.json
. These files can be aliased in your STATICFILES
directory in config/filesystem.py
. They do not have to be in the root of your project but instead could be in a storage/root
or storage/public
directory and aliased with a simple /
.
For example a basic setup would have this as your directory:
and you can alias this in your STATICFILES
constant:
You will now be able to access localhost:8000/robots.txt
and you will have your robots.txt served correctly and it can be indexed by search engines properly.
Thats it! Static files are extremely simple. You are now a master at static files!
.send_from('Masonite Bot', icon="")
This type of protection prevents cross site forgery. In order to activate this feature, we also need to add the . Copy and paste the middleware into your project under the app/http/middleware/CsrfMiddleware.py
file.
There has been a slight change in the constants used in the file. Mainly just for consistency and coding standards. Your file may have some slight changes but this change is optional. If you do make this change, be sure to change any places in your code where you have used the Orator Query Builder. For example any place you may have:
You can check for what the class should look like from the repository
Masonite 2 comes with a new autoloader. This can load all classes in any directory you specify right into the when the server first starts. This is incredibly useful for loading your models, commands or tasks right into the container.
Be caution that this will autoload all models into the with the class name as the key and the class as the binding.
Be sure to read about all the to ensure that your application is completely up to date with many of the latest decisions and be sure to thoroughly test your application. Feel free to open an issue if any problems arise during upgrading.
That is all the main changes in 2.1. Go ahead and run your server and you should be good to go. For a more up to date list on small improvements that you can make in your application be sure to checkout the documentation article.
Read for any futher changes you may want or have to make to your code base.
You can do a lot of other awesome things like rule enclosures. Read more under the
Here is an example application that is being upgraded from 2.1 to 2.2
This guide is designed to give you as much information as possible to upgrade your application from Masonite 2.3 to Masonite 3.0.
We will go through each new breaking change in Masonite and code examples on how to upgrade the code to use Masonite 3. If there is any code breaking during the upgrade, please go to our Slack channel and let us know so we can add more information to this documentation.
This document will be broken down into 2 parts, upgrading from Orator to Masonite and upgrading from Masonite 2.3 to 3.0.
Before you go through this document it is highly recommended that you read Whats New in Masonite 3.
Note that there are some large changes from 2.3 to 3.0. Depending on the size of your project it might make more sense to rebuild your project and port your code to a new Masonite 3 app. This guide is for changing your existing projects.
We need to uninstall some packages and install some others
First uninstall masonite and orator and install masonite 3:
That should be all you need to get the requirements up to date.
At this point Masonite will not work because our codebase is not updated to work with Masonite 3
Now we can start changing the application to get our app to run again.
The request class is now not even initialized until a request is sent. Previously the request class acted as a singleton but it is now a new class that is initialized on every request. Because of this change, any place you previously had been fetching the request class before the request is sent will no longer work. This could be in several places but is likely most common to be in any class __init__
methods that are initialized before a request was sent, like in a service provider where the wsgi
attribute is False
.
The response headers are now set on the response class. This makes much more sense now. Previously we set them on the request class which really didn't make sense.
Any place in your code where you want a header to be on the response. The code changes may look something like this:
Should now be written as:
Some headers used to have to be prefixed as HTTP_
to be used correctly. This is no longer required. Code where you have done this:
Can be changed to:
Status codes used to be set on the request class but is now set on the response class. The method and uses are the same:
Should be changed to:
Because of the request singleton change we had to offset some other instantiation so we added 2 new providers you need to add. The placement of the RequestHelpersProvider
and CsrfProvider
needs to be between the AppProvider
and the RouteProvider
. The change is in the config/providers.py
file.
We also need to add the orm provider from Masonite ORM. It doesn't really matter where add this so we can add it at the bottom of the list somewhere:
There used to be a bootstrap/start.py
file in Masonite apps. This file contained a method was used to handle the request and response lifecycle. There was no reason to keep the method inside the application since it was such a low level method and was crucial for the framework to work.
So we need to import the method from the Masonite codebase instead of the start.py file. We also renamed the method response_handler
instead of app
You may not have to make this change but in some previous versions of Masonite, the craft file that came with new projects was wrong. The file is called craft
and is in the root of your project directory. If the changes with the + sign already exist then your project is correct. If you have the code in strikethrough then make this diff change:
If you use Masonite queues, there are 3 new columns on the queue_jobs
table. Please make a migration and add these 3 columns:
First make the migration
Then add the migration:
Then run the migration
If you used any helper routes inside your web.py file, these have been removed. You will now need to use only route classes:
If you have code inside your web.py
You will need to remove this import and use the class based routes:
This applied for the helpers: get
, post
, put
, patch
, delete
and group
helpers.
In previous versions of Masonite, Masonite would set flashed messages for a 2 second expiration time. This caused numerous different issues like what happens when a page redirection took longer than 2 seconds or what happens when the client times are not synced correctly.
Now we have took a "get and delete" approach. So now the flash data is deleted when it is retrieved. This means that flash data can stay in session until it is fetched.
To do this we have a new method for the "get and delete" of flash data.
If you are using the bag()
helper in your templates then this:
If you are using the session()
helper than you will need to take a similiar approach:
For upgrading from Orator to Masonite please read the Orator to Masonite ORM guide
Masonite 4 is the biggest change in a Masonite release we have ever had. Smaller applications may benefit from creating a new app and then copying and pasting controllers, routes and views to the new installation.
For medium to large scale applications, you will need to go through the codebase and upgrade everything to use the new structures we have available in Masonite 4.
It is highly recommended that you start at Masonite 3 before upgrading to Masonite 4. If you are running any version less than Masonite 3 then please use the upgrade guide to upgrade to each version first.
Masonite 4 drops support for Python 3.6. You will have to upgrade to Python 3.7+ in order to run Masonite
First step of the upgrade guide is to uninstall Masonite 3 and install Masonite 4:
The next step of the upgrade guide is to replace your craft file. This is a basic file in the root of your project that will be used whenever you run python craft
. We will use this to keep testing our server:
Go to this file and copy and paste it into your own craft file in the root of your project. If you are running Masonite 3 then you should already have this file.
Masonite 4 has a new Kernel.py
file which is used to customize your application and contains necessary bootstrapping to start your application.
You will also need to put this into the base of your project.
Go to this file and paste it into your own Kernel.py
file in the root of your project.
Now go through this file and customize any of the locations. Masonite 4 uses a different file structure than Masonite 3. For example, Masonite 3 put all views in resources/templates
while Masonite 4 has them just in templates
.
Because of this, you can either change where the files are located by moving the views to a new templates
directory or you can change the path they are registered in your Kernel:
Go to your register_configurations
method in your Kernel and inside your register_templates
method you can change it to
Go through the rest of the methods and make sure the paths are set correctly.
Add the following providers to your project:
Routes have changed slightly.
First, routes are now all under the Route
class. The route classes have moved into methods:
The WSGI file has also changed.
Go to this file and replace your own wsgi.py
file
The import path has changed for the following:
Previously when we had to do a redirection we would use the request class:
This has now been changed to the response class:
Same applies to back redirection:
Middleware has been moved to this new Kernel file. Middleware now works a little different in M4. Middleware has changed in the following ways:
middleware no longer needs an __init__
method.
Middleware requires the request and response parameters inside the before and after methods.
Middleware requires either the request or response to be returned
Middleware will change in the following example:
Old:
To this:
Masonite 4 uses the same Masonite ORM package as Masonite 3 but changes a lot of the authentication.
Inherit a new Authenticates
class
Authentication has also changes slightly. Whenever you are logging in a user the following UI has changed:
Controllers have not changes much but in order for routes to pick up your string controllers, you must inherit from Masonite controller class:
The rest of your controller structure remains the same.
Add the following to the config/application.py
file.
Change the AUTH
constant to the new GUARDS
configuration:
Add a new config/exceptions.py file:
The config/storage.py
file has been replaced with a config/filesystem.py
file:
Go to this file and copy it into your project. Then move the STATICFILES
from your storage config into this new filesystem config
Change the config/session.py
to the following:
After you upgrade all your middleware, you will need to move them from the config/middleware.py file to the top of your Kernel file:
get_flashed
method has changed to just get
. Here is an example in your templates:
The static helper has changed to asset
:
Finally after all the above changes attempt to run your server:
Masonite 5 brings new changes ... TODO
Masonite 5 adds support for Python 3.11.
First step of the upgrade guide is to uninstall Masonite 4 and install Masonite 5:
The import path has changed for the following:
Rename memcache
driver to memcached
to the config/cache.py
file.
Two new providers have been created, that you will need to add them in config/providers.py
file.
Memcached driver name has been fixed. It was called Memcache
as its real name is Memcached
. When using this driver you will now need to access it like this:
or via the facade:
All Masonite helpers can now be imported from helpers
module to ease development experience.
A new helper to access application container app
has been introduced ! More information in Helpers documentation.
Logging is a pretty crucial part of any application. Logging allows you to see errors your application is throwing as well as allow you to log your own messages in several different alert levels.
Masonite Logging currently contains the ability to log to a file, syslog and slack.
To enable logging in your application, ensure that LoggingProvider
is added to your application providers:
Masonite has a simple yet powerful mail feature which is used to send emails from your application.
To create and send an email with Masonite, you must first built a Mailable
class. This class will act as the settings for your email such as the address you are sending from, the subject, the text of the email, the html of the email, etc.
All of these settings on the mailable can be changed or overwritten when you are ready to send you email, more on this later on.
The first step of building a mailable is running the command:
This will create your mailable and it will look something like this:
You can send your mailable inside your controller easily by resolving the Mail
class in your controller:
Notice at this point you can call any building options you want on the mailables to modify the behavior of it before sending.
Note that you can also use the Mail
facade anywhere in your code:
You can modify the behavior of the mailable by using any one of these options
Sending attachments is really simply with Masonite. Simply attach the file to the mailable before sending it:
You will then see your attachment in the email.
When you are building your emails it might be nice to see how they look before sending them. This can save a lot of time when you're trying to get those styles just right.
You can simply return your mailable in your controller and it will return like a normal view file.
If you are using the view()
option in your mailable then you will need to set the application on the mailable:
You can change the driver which sends the email by using the driver
argument in the send()
method:
Options | Description |
---|---|
to('user@example.com')
Specifies the user to send the email to.
You may also specify the users name like to('Joseph <user@example.com>')
.
from_("admin@example.com")
Specifies the address that the email should appear it is from.
cc(["accounting@example.com"])
A list of the addresses that should be "carbon copied" onto this email
bcc(["accounting@example.com"])
A list of the addresses that should be "blind carbon copied" onto this email
subject('Subject of the Email')
Specifies the subject of the email.
reply_to('customers@example.com')
Specifies the address that will be set if a user clicks reply to this email
text('Welcome to Masonite')
Specifies the text version of the email.
html('Welcome to Masonite')
Specifies the HTML version of the email.
view('mailables.view', {})
Specifies a view file with data to render the HTML version of the email
priority(1)
Specifies the priority of the email, values should be 1 through 5.
low_priority()
Sets the priortiy of the email to 5.
high_priority()
Sets the priortiy of the email to 1.
attach('MAY.pdf', 'path/invoice.pdf')
Attaches a file to the email.
Welcome to Masonite 2.3! In this guide we will walk you through how to upgrade your Masonite application from version 2.2 to version 2.3.
In this guide we will only be discussing the breaking changes and won't talk about how to refactor your application to use the awesome new features that 2.3 provides. For that information you can check the Whats New in 2.3 documentation to checkout the new features and how to refactor.
We'll walk through both Masonite upgrades and breaking packages as well
Craft is now a part of Masonite core so you can uninstall the masonite-cli tool. You now no longer need to use that as a package.
This is a little weird but we'll get craft back when we install Masonite 2.3
Next, we can upgrade your Masonite version. Depending on your dependancy manager it will look something like this:
Change it from this:
to
Go ahead and install masonite now:
Masonite changed the way the response is generated internally so you will need to modify how the response is retrieved internally. To do this you can go to your bootstrap/start.py
file and scroll down to the bottom.
Change it from this:
to this:
This will allow Masonite to better handle responses. Instead of converting everything to a string like the first snippet we can now return bytes. This is useful for returning images and documents.
Previously Masonite used several packages and required them by default to make setting everything up easier. This slows down package development because now any breaking upgrades for a package like Masonite Validation requires waiting for the the next major upgrade to make new breaking features and improve the package.
Now Masonite no longer requires these packages by default and requires you as the developer to handle the versioning of them. This allows for more rapid development of some of Masonite packages.
Masonite packages also now use SEMVER versioning. This is in the format of MAJOR.MINOR.PATCH
. Here are the required versions you will need for Masonite 2.3:
masonite-validation>=3.0.0
masonite-scheduler>=3.0.0
These are the only packages that came with Masonite so you will need to now manage the dependencies on your own. It's much better this way.
Masonite now uses a concept called guards so you will need a quick crash course on guards. Guards are simply logic related to logging in, registering, and retrieving users. For example we may have a web
guard which handles users from a web perspective. So registering, logging in and getting a user from a database and browser cookies.
We may also have another guard like api
which handles users via a JWT token or logs in users against the API itself.
Guards are not very hard to understand and are actually unnoticable unless you need them.
In order for the guards to work properly you need to change your config/auth.py
file to use the newer configuration settings.
You'll need to change your settings from this:
to this:
To manage the guards (and register new guards) there is the new AuthenticationProvider
that needs to be added to your providers list.
Masonite no longer supports SASS and LESS compiling. Masonite now uses webpack and NPM to compile assets. You will need to now reference the Compiling Assets documentation.
You will need to remove the SassProvider
completely from the providers list in config/providers.py
. As well as remove the SassProvider
import from on top of the file.
You can also completely remove the configuration settings in your config/storage.py
file:
Be sure to reference the Compiling Assets documentation to know how to use the new NPM features.
The container can no longer hold modules. Modules now have to be imported in the class you require them. For example you can't bind a module like this:
and then make it somewhere else:
This will throw a StrictContainerError
error. Now you have to import it so will have to do something like this using the example above:
Now that we can no longer bind modules to the container we need to make some changes to the wsgi.py
file because we did that here.
Around line 16 you will see this:
Just completely remove that line. Its no longer needed.
Also around line 19 you will see this line:
You can completely remove that as well.
Lastly, around line 31 you can change this line:
to this:
It's unlikely this effects you and query string parsing didn't change much but if you relied on query strings like this:
/?filter[name]=Joe&filter[user]=bob&email=user@email.com
Or html elements like this:
then query strings will now parse to:
You'll have to update any code that uses this. If you are not using this then don't worry you can ignore it.
Not many breaking changes were done to the scheduler but there are alot of new features. Head over to the Whats New in Masonite 2.3 section to read more.
We did change the namespace from scheduler
to masonite.scheduler
. So you will need to refactor your imports if you are using the scheduler.
You should be all good now! Try running your tests or running craft serve
and browsing your site and see if there are any issues you can find. If you ran into a problem during upgrading that is not found in this guide then be sure to reach out so we can get the guide upgraded.