The Definitive Guide to Masonite. Building Web Applications with Python (Pitt) 1 ed (2020)

До загрузки: 30 сек.



Благодарим, что скачиваете у нас :)

Если, что - то:

  • Поделится ссылкой:
  • Документ найден в свободном доступе.
  • Загрузка документа - бесплатна.
  • Если нарушены ваши права, свяжитесь с нами.
Формат: pdf
Найдено: 19.06.2020
Добавлено: 30.09.2020
Размер: 3.17 Мб

The Definitive
Guide to
Masonite
Building Web Applications with Python

Christopher Pitt
Joe Mancuso

The Definitive Guide to
Masonite
Building Web Applications
with Python
Christopher Pitt
Joe Mancuso

Christopher Pitt
Cape Town, South Africa
Joe Mancuso
Holbrook, NY, USA
The Definitive Guide to Masonite: Building Web Applications with Python
ISBN-13 (pbk): 978-1-4842-5601-5 ISBN-13 (electronic): 978-1-4842-5602-2
https://doi.org/10.1007/978-1-4842-5602-2
Copyright © 2020 by Christopher Pitt and Joe Mancuso
This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the
material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation,
broadcasting, reproduction on microfilms or in any other physical way, and transmission or information
storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now
known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with
every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an
editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not
identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to
proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of publication,
neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or
omissions that may be made. The publisher makes no warranty, express or implied, with respect to the
material contained herein.
Managing Director, Apress Media LLC: Welmoed Spahr
Acquisitions Editor: Steve Anglin
Development Editor: Matthew Moodie
Coordinating Editor: Mark Powers
Cover designed by eStudioCalamar
Cover image designed by Raw Pixel (www.rawpixel.com)
Distributed to the book trade worldwide by Apress Media, LLC, 1 New York Plaza, New York, NY 10004,
U.S.A. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@springer-sbm.com, or visit www.
springeronline.com. Apress Media, LLC is a California LLC and the sole member (owner) is Springer Science
+ Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation.
For information on translations, please e-mail editorial@apress.com; for reprint, paperback, or audio rights,
please email bookpermissions@springernature.com.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and
licenses are also available for most titles. For more information, reference our Print and eBook Bulk Sales
web page at http://www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is available to
readers on GitHub via the book's product page, located at www.apress.com/9781484256015. For more
detailed information, please visit http://www.apress.com/source-code.
Printed on acid-free paper

iii
Chapter 1 : Getting Started ���������������������������������������������������������������������������������������� 1
“Where Do I Begin?” ��������������������������������������������������������������������������������������������������������������������� 1
How Masonite Handles Releases �������������������������������������������������������������������������������������������������� 2
Should I Upgrade? ������������������������������������������������������������������������������������������������������������������� 2
What Does an Upgrade Entail? ������������������������������������������������������������������������������������������������ 3
How Often Does This Happen? ������������������������������������������������������������������������������������������������ 3
Installing the Dependencies ���������������������������������������������������������������������������������������������������������� 4
Installing Python and MySQL on macOS ���������������������������������������������������������������������������������� 4
Installing Python and MySQL on Linux ������������������������������������������������������������������������������������ 6
Editing Code ���������������������������������������������������������������������������������������������������������������������������� 6
Setting Up in Other Environments ������������������������������������������������������������������������������������������� 7
Creating a New Masonite Application ������������������������������������������������������������������������������������������� 7
Exploring the Masonite Folder Structure ��������������������������������������������������������������������������������� 9
Planning the Example Application ����������������������������������������������������������������������������������������������� 11
Summary ������������������������������������������������������������������������������������������������������������������������������������� 12
Chapter 2 : Routing �������������������������������������������������������������������������������������������������� 13
“How Does Masonite Handle Requests?” ����������������������������������������������������������������������������������� 13
Creating Controllers and Routes ������������������������������������������������������������������������������������������������� 14
Creating Different Kinds of Routes ���������������������������������������������������������������������������������������������� 19
Different Methods ������������������������������������������������������������������������������������������������������������������ 19
Different Parameters ������������������������������������������������������������������������������������������������������������� 20
Route Groups ������������������������������������������������������������������������������������������������������������������������� 24
Table of Contents
About the Authors ���������������������������������������������������������������������������������������������������� xi
About the T echnical Reviewers ����������������������������������������������������������������������������� xiii

iv
Exploring Request and Response ����������������������������������������������������������������������������������������������� 25
Reading and Writing Cookies ������������������������������������������������������������������������������������������������� 26
Sending Other Kinds of Responses ��������������������������������������������������������������������������������������� 27
Creating Views ���������������������������������������������������������������������������������������������������������������������������� 28
Starting the Example Application ������������������������������������������������������������������������������������������������ 30
Summary ������������������������������������������������������������������������������������������������������������������������������������� 36
Chapter 3 : The Service Container ��������������������������������������������������������������������������� 37
The Problem We Are Solving ������������������������������������������������������������������������������������������������������� 38
Type Hinting �������������������������������������������������������������������������������������������������������������������������������� 39
Service Providers ������������������������������������������������������������������������������������������������������������������ 40
The Register Method ������������������������������������������������������������������������������������������������������������� 42
The Boot Method ������������������������������������������������������������������������������������������������������������������������� 43
The WSGI Attribute ���������������������������������������������������������������������������������������������������������������������� 44
More on Binding �������������������������������������������������������������������������������������������������������������������������� 45
Class vs � Object Behavior ������������������������������������������������������������������������������������������������������ 45
Binding Singletons ����������������������������������������������������������������������������������������������������������������� 47
Simple Binding ���������������������������������������������������������������������������������������������������������������������� 47
Resolving Classes ����������������������������������������������������������������������������������������������������������������������� 48
Hooks ������������������������������������������������������������������������������������������������������������������������������������������ 49
On Make �������������������������������������������������������������������������������������������������������������������������������� 49
On Bind ���������������������������������������������������������������������������������������������������������������������������������� 49
On Resolve ����������������������������������������������������������������������������������������������������������������������������� 50
Swapping ������������������������������������������������������������������������������������������������������������������������������������ 50
Design Pattern Knowledge ���������������������������������������������������������������������������������������������������������� 51
Coding to Abstractions ����������������������������������������������������������������������������������������������������������� 51
Dependency Injection ������������������������������������������������������������������������������������������������������������ 52
Inversion of Control (IoC) ������������������������������������������������������������������������������������������������������� 52
Implementing Abstractions ��������������������������������������������������������������������������������������������������������� 53
Setting Up Our Abstraction ���������������������������������������������������������������������������������������������������� 53
Swapping Out Loggers ���������������������������������������������������������������������������������������������������������� 54
Table of Con Ten Ts

v
Remembering ����������������������������������������������������������������������������������������������������������������������������� 55
Collecting ������������������������������������������������������������������������������������������������������������������������������������ 55
Collecting by Key ������������������������������������������������������������������������������������������������������������������� 55
Collecting Objects ������������������������������������������������������������������������������������������������������������������ 56
Application ���������������������������������������������������������������������������������������������������������������������������������� 57
The Package �������������������������������������������������������������������������������������������������������������������������� 57
Abstraction Class ������������������������������������������������������������������������������������������������������������������� 57
Concrete Class ����������������������������������������������������������������������������������������������������������������������� 58
The Service Provider ������������������������������������������������������������������������������������������������������������� 58
The Controller ������������������������������������������������������������������������������������������������������������������������ 59
Refresher on Abstractions and Concretions �������������������������������������������������������������������������� 61
The Route ������������������������������������������������������������������������������������������������������������������������������ 61
Chapter 4 : Accepting Data with Forms ������������������������������������������������������������������� 63
“How Do I Store Data?” ��������������������������������������������������������������������������������������������������������������� 63
Building Secure Forms ���������������������������������������������������������������������������������������������������������������� 64
Template Conditionals ����������������������������������������������������������������������������������������������������������� 66
Template Filters ��������������������������������������������������������������������������������������������������������������������� 68
CSRF Protection ��������������������������������������������������������������������������������������������������������������������� 69
Validating Form Data ������������������������������������������������������������������������������������������������������������������� 71
Fetching Remote Data ����������������������������������������������������������������������������������������������������������������� 74
Resolving Dependencies from the Container ������������������������������������������������������������������������ 77
Summary ������������������������������������������������������������������������������������������������������������������������������������� 80
Chapter 5 : Using a Database ���������������������������������������������������������������������������������� 81
How Do I Store Data? ������������������������������������������������������������������������������������������������������������������ 81
Keeping the Database in Code ���������������������������������������������������������������������������������������������������� 81
Filling the Database with Dummy Data ��������������������������������������������������������������������������������������� 86
Writing to the Database �������������������������������������������������������������������������������������������������������������� 88
Reading from the Database �������������������������������������������������������������������������������������������������������� 90
Filtering Database Data ��������������������������������������������������������������������������������������������������������������� 92
Updating Database Data ������������������������������������������������������������������������������������������������������������� 95
Table of Con Ten Ts

vi
Deleting Database Data �������������������������������������������������������������������������������������������������������������� 97
Simplifying Code Through Models ���������������������������������������������������������������������������������������������� 99
Summary ����������������������������������������������������������������������������������������������������������������������������������� 103
Chapter 6 : Security ����������������������������������������������������������������������������������������������� 105
CSRF Protection ������������������������������������������������������������������������������������������������������������������������ 105
Cleaning Request Input ������������������������������������������������������������������������������������������������������������� 106
The CSRF Token ������������������������������������������������������������������������������������������������������������������������ 107
Form Submissions �������������������������������������������������������������������������������������������������������������������� 107
AJAX Calls ��������������������������������������������������������������������������������������������������������������������������������� 108
Password Reset ������������������������������������������������������������������������������������������������������������������������ 109
The Form ����������������������������������������������������������������������������������������������������������������������������������� 110
Unicode Attack �������������������������������������������������������������������������������������������������������������������������� 110
SQL Injection ����������������������������������������������������������������������������������������������������������������������������� 111
Query Binding ��������������������������������������������������������������������������������������������������������������������������� 112
Mass Assignment ���������������������������������������������������������������������������������������������������������������������� 112
Fillable �������������������������������������������������������������������������������������������������������������������������������������� 114
CORS ����������������������������������������������������������������������������������������������������������������������������������������� 115
CORS Provider ��������������������������������������������������������������������������������������������������������������������������� 115
Secure Headers ������������������������������������������������������������������������������������������������������������������������� 117
Meaning of the Headers ������������������������������������������������������������������������������������������������������������ 117
Using the Secure Middleware ��������������������������������������������������������������������������������������������������� 118
Releases ������������������������������������������������������������������������������������������������������������������������������������ 119
CVE Alerts ���������������������������������������������������������������������������������������������������������������������������������� 119
Encryption ��������������������������������������������������������������������������������������������������������������������������������� 120
Encoding vs � Encryption ������������������������������������������������������������������������������������������������������������ 121
Password Encryption ���������������������������������������������������������������������������������������������������������������� 121
Cookies ������������������������������������������������������������������������������������������������������������������������������������� 121
Signing on Your Own ����������������������������������������������������������������������������������������������������������������� 122
Table of Con Ten Ts

vii
Chapter 7 : Authentication ������������������������������������������������������������������������������������� 123
How do I Authenticate Users? ��������������������������������������������������������������������������������������������������� 123
Using the Tools Provided ����������������������������������������������������������������������������������������������������������� 123
Wait, Haven’t I Seen “User” Things Already? ���������������������������������������������������������������������������� 125
How Do I Use Different Fields? ������������������������������������������������������������������������������������������������� 127
How Do I Log the User in Automatically? ���������������������������������������������������������������������������������� 129
Logging the User Out ���������������������������������������������������������������������������������������������������������������� 129
Using Tokens (JWT) Instead of Credentials ������������������������������������������������������������������������������� 130
How Do I Protect Parts of My App? ������������������������������������������������������������������������������������������� 130
How Do I Ensure Valid Emails? �������������������������������������������������������������������������������������������������� 132
Summary ����������������������������������������������������������������������������������������������������������������������������������� 134
Chapter 8 : Creating Middleware ��������������������������������������������������������������������������� 135
Constructing Middleware ���������������������������������������������������������������������������������������������������������� 135
Initializer ������������������������������������������������������������������������������������������������������������������������������ 135
The before Method �������������������������������������������������������������������������������������������������������������� 136
The after Method ����������������������������������������������������������������������������������������������������������������� 136
Registering Middleware ������������������������������������������������������������������������������������������������������������ 137
HTTP Middleware ���������������������������������������������������������������������������������������������������������������� 137
Route Middleware ��������������������������������������������������������������������������������������������������������������������� 138
Using the Middleware ��������������������������������������������������������������������������������������������������������������� 138
Middleware Stacks �������������������������������������������������������������������������������������������������������������� 140
Chapter 9 : Using Helpers �������������������������������������������������������������������������������������� 143
The Request and Auth Helpers �������������������������������������������������������������������������������������������������� 143
The Route Helper ���������������������������������������������������������������������������������������������������������������������� 145
The Container and Resolve Helpers ������������������������������������������������������������������������������������������ 146
The Env and Config Helpers ������������������������������������������������������������������������������������������������������ 147
The Dump-and-Die Helper �������������������������������������������������������������������������������������������������������� 148
Summary ����������������������������������������������������������������������������������������������������������������������������������� 148
Table of Con Ten Ts

viii
Chapter 10 : Doing Work in the Background ��������������������������������������������������������� 149
How Do I Speed Up My Application with Queues? �������������������������������������������������������������������� 149
When and Where Do I Use Queueing? ��������������������������������������������������������������������������������� 151
Finding Files to Cache ��������������������������������������������������������������������������������������������������������������� 151
Creating Jobs ���������������������������������������������������������������������������������������������������������������������������� 154
Downloading Files ��������������������������������������������������������������������������������������������������������������� 157
Showing Downloaded Files ������������������������������������������������������������������������������������������������� 159
Using Different Queue Providers ����������������������������������������������������������������������������������������������� 160
AMPQ/RabbitMQ ������������������������������������������������������������������������������������������������������������������ 160
Database ����������������������������������������������������������������������������������������������������������������������������� 161
Summary ����������������������������������������������������������������������������������������������������������������������������������� 162
Chapter 11 : Adding Websockets with Pusher ������������������������������������������������������� 163
“Isn’t That Ajax?” ���������������������������������������������������������������������������������������������������������������������� 163
Installing Pusher ����������������������������������������������������������������������������������������������������������������������� 164
Integrating Pusher on the Front End ������������������������������������������������������������������������������������ 165
Creating Commands ������������������������������������������������������������������������������������������������������������ 167
Integrating Pusher on the Back End ������������������������������������������������������������������������������������ 170
Acting on Received Messages �������������������������������������������������������������������������������������������������� 173
Summary ����������������������������������������������������������������������������������������������������������������������������������� 174
Chapter 12 : Testing ����������������������������������������������������������������������������������������������� 175
What Is Integration Testing? ������������������������������������������������������������������������������������������������������ 175
Wh y Tests in the First Place? ���������������������������������������������������������������������������������������������������� 175
Where Do Our Tests Live? ��������������������������������������������������������������������������������������������������������� 176
Creating Tests ��������������������������������������������������������������������������������������������������������������������������� 177
Our First Test ����������������������������������������������������������������������������������������������������������������������� 178
Given When Then ���������������������������������������������������������������������������������������������������������������������� 179
Test-Driven Development ���������������������������������������������������������������������������������������������������������� 180
Table of Con Ten Ts

ix
Factories ����������������������������������������������������������������������������������������������������������������������������������� 181
Creating Factories ��������������������������������������������������������������������������������������������������������������� 181
Using Factories �������������������������������������������������������������������������������������������������������������������� 182
Asserting Database Values �������������������������������������������������������������������������������������������������������� 183
Testing Environments ���������������������������������������������������������������������������������������������������������������� 183
Chapter 13 : Deploying Masonite ��������������������������������������������������������������������������� 185
The Request Life Cycle �������������������������������������������������������������������������������������������������������������� 185
The Web Server ������������������������������������������������������������������������������������������������������������������������� 187
Setting Up the Server ���������������������������������������������������������������������������������������������������������� 188
Web Server Software ���������������������������������������������������������������������������������������������������������������� 191
Installing NGINX ������������������������������������������������������������������������������������������������������������������� 192
Setting Up Python Software ������������������������������������������������������������������������������������������������������ 193
Configuring NGINX �������������������������������������������������������������������������������������������������������������������� 194
Reloading NGINX ������������������������������������������������������������������������������������������������������������������ 196
Set Up Tasks ������������������������������������������������������������������������������������������������������������������������ 197
Setting Up Our Application �������������������������������������������������������������������������������������������������������� 197
Running the Application ������������������������������������������������������������������������������������������������������������ 198
Deployments ����������������������������������������������������������������������������������������������������������������������������� 200
Manual Deployments ����������������������������������������������������������������������������������������������������������� 200
Automatic Deployments ������������������������������������������������������������������������������������������������������ 201
Index ��������������������������������������������������������������������������������������������������������������������� 203
Table of Con Ten Ts

xi
About the Authors
Christopher Pitt  is a developer and writer, working at
Indiefin. He usually works on application architecture,
though sometimes you’ll find him building compilers or
robots. He is also the author of several web development
books and is a contributor on various open source projects
such as AdonisJs and Masonite.
Joseph Mancuso  is the creator, maintainer, and BDFL for
Masonite. He has been a software developer for over a
decade and the CEO of Masonite X Inc, a company based
around all things Masonite related. He’s an avid contributor
of open source, a consultant for Masonite-adopted
companies, and freelance worker for companies using
Masonite.

xiii
About the Technical Reviewers
Alfredo Aguirre  is a senior software engineer who helps a
wide range of online companies such as Mozilla, Google,
R/GA, Elsewhen, and Stink Studios to transform their ideas
into digital products while ensuring they perform and scale
to meet the business requirements. He is based in London,
UK, and can be contacted at https://madewithbytes.com/ .
Vaibhav Mule  has been programming in Python for 5 years at various organizations. He
started off as an early adopter for the Masonite framework and is actively involved in
maintaining Masonite and Masonite ORM.
You can learn more about him at vaibhavmule.com .

1 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_1
CHAPTER 1
Getting Started
By writing this book, we hope to teach you how to build great applications, using the
Masonite framework ( https://github.com/masoniteframework/masonite ). Masonite
is a modern Python framework, which includes tools and conventions aimed at making
that task easier.
It’s ok if you’re new to Python. It’s ok if you’re new to Masonite. The book is written so
that you can still get the most out of it. There may be some more advanced topics, along the
way, but we’ll do our best to make them additional and not essential to your experience.
If you’ve never used Python, we recommend this short course to get you familiar
with the basics: https://teamtreehouse.com/library/python-basics-3 .
The things you’ll need are a computer on which to install Python, some time to read,
and some time to experiment. It doesn’t need to be a fancy computer, and it doesn’t
need to be a huge amount of time.
We’ve arranged the chapters so that you can learn about core concepts of the framework
and build toward a functional application. You can read this book as a reference guide. You
can read it as a series of tutorials. You can read it one chapter at a time, or altogether.
Thank you for taking the first step toward becoming a Masonite pro.
“Where Do I Begin?”
There are many different kinds of programming. The one you’re probably most familiar
with is application programming, which is where you install an application (or use an
installed application) on a phone, tablet, or computer.
Come to think of it, you’re also probably familiar with another kind: web site
programming. These kinds of programs are used through a web browser like Chrome,
Safari, or Firefox.

2
Masonite sits somewhere in the middle of these two kinds of programs. Let me
explain why.
Python was originally designed as a system programming language. That means
it was intended to be used in short server administration scripts, to configure other
software and to perform batch operations.
Over time it has become a powerful, multi-paradigm programming language. Some
programming languages are mainly used for web programming, and they are used
through web servers, like Apache and Nginx. Other languages take more complete
control over how a web server behaves. Python is one of the latter languages.
Python web applications, and Masonite applications in particular, are usually
responsible for everything from listening on a port to interpreting an HTTP request
to sending an HTTP response. If something’s wrong with a Masonite application,
something’s wrong with the whole server. With this increase in risk comes an increase in
flexibility.
In addition, controlling the whole server gives us the ability to do more advanced
things, like serving web sockets and interacting with external devices (like printers and
assembly lines).
How Masonite Handles Releases
Before we look at code, it’s important to talk about how Masonite handles releases. Big
frameworks, like Masonite, change quickly. You may start a project on version 2.1, but in
a few weeks version 2.2 is released. This can lead to some important questions:
• Should I upgrade?
• What does an upgrade entail?
• How often does this happen?
Let’s answer these, one at a time.
Should I Upgrade?
Upgrades are a good thing, but sometimes they have trade-offs.
Functionality may be deprecated, meaning it is flagged for future removal. There
may be multiple changes you need to make, so that your application will work in the new
version. You may uncover bugs or things you’ve not tested for.
Cha Pter 1 Gett InG Started

3
Having said all that, upgrades can also bring new features and security fixes. The
security benefits alone are reason enough to take any upgrade seriously.
The best thing to do is review the upgrade guide and decide whether the cost of the
upgrade is worth the benefits it brings. There’s no harm in staying a few major versions
back, so long as you’re still using a minor version of the framework that can receive
security updates (and so long as there are no obvious security issues with the version
you’re using).
{ou can find an up-to-date upgrade guide on the documentation web site:
https://docs.masoniteproject.com/upgrade-guide .
What Does an Upgrade Entail?
This question is easily answered by reading through the upgrade guide. If you’re a few
versions behind, you may need to go through multiple upgrade guides to get fully
up-to- date.
Masonite uses a three-part version scheme: PARADIGM.MAJOR.MINOR .
This means you should have little-to-no issues when upgrading from 2.1.1 to 2.1.2 .
Upgrades from 2.1 to 2.2 are a bit more involved, but I've found they generally take 10
minutes or less, assuming I've not veered too far from the conventions of the framework.
In contrast to this versioning scheme, each Masonite library uses Semantic
Versioning ( https://semver.org ). If you’re using individual Masonite libraries,
as opposed to the whole framework, then you’re pretty safe upgrading from 2.1 to
2.2 without breaking changes.
How Often Does This Happen?
Masonite follows a 6-month release cycle. This means you can expect a new MAJOR
version every 6 months. These releases aim to require less than 30 minutes to upgrade.
If they’re expected to take more than that, they’re shifted into a new PARADIGM
version.
Cha Pter 1 Gett InG Started

4
Installing the Dependencies
Masonite needs a few dependencies to work well. While we’re installing those, we may
also talk a bit about how best to write Python code. To begin with, let’s install Python.
Installing Python and MySQL on macOS
I work on a Mac, so that’s where I’d like to start things off. macOS doesn’t come with
a command-line package manager (like what you’d expect in Linux), so we’ll need to
install one.
For this section, we’re going to assume you have a recent version of macOS
installed, with access to the Internet.
Open Safari, and go to https://brew.sh . This is the home of Homebrew. It’s a great
package manager, which will provide us with ways to install Python 3 and a database.
There’s a command, front and center. It should look something like
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/
install/master/install)"
It says to download a Ruby script from that URL, executing it with the Ruby
interpreter most macOS systems have installed. If you’re feeling particularly suspicious,
feel free to open that URL in Safari and inspect its contents.
It’s good to be suspicious of sites that tell you to blindly execute scripts from the
Internet. In this case, homebrew has a reputation for security and utility. they’re
balancing ease of installation against the potential for that suspicion.
If you still consider this too risky, check toward the end of this section, where I
recommend a more in-depth resource for setting up new Python environments.
Running this command, in terminal, will begin the process of installing Homebrew.
It takes a bit of time and will ask questions along the way. One of those questions is
whether you would like to install the Xcode command-line tools.
Cha Pter 1 Gett InG Started

5
You don’t really have a choice, if you want to be able to use Homebrew. Those
utilities include compilers for the code Homebrew downloads, so it won’t be able to
install much without them.
When the installation is complete, you should be able to start installing
dependencies through Homebrew. The ones we’re interested in are Python 3 and
MySQL 5.7. Let’s install them:
$ brew install python3
$ brew install mysql@5.7
after installing MySQL, you’ll be given some instructions for starting the server.
I recommend you follow these, or you won’t be able to log in or make changes to
the database.
You can verify the version of Python and MySQL by running
$ python --version
$ mysql --version
You should see Python 3.x.x and mysql ... 5.7.x installed.
If that command tells you that you’re still using Python 2.x , then you may need to
add the path, suggested at the end of the python3 installation, to your PATH variable.
Mine looked something like this:
export PATH="/usr/local/opt/python/libexec/bin:$PATH"
this is from ~/.zshrc , but you should put it in ~/.profile or ~/.bashrc ,
depending on how your system is set up.
Run the --version commands, in a new terminal window, and you should see
Python 3.x.x as the version.
Cha Pter 1 Gett InG Started

6
Installing Python and MySQL on Linux
Next up, we’re going to look at how to install these dependencies on Debian/Ubuntu
Linux. Here, we have access to a command-line package manager, called aptitude.
You can use the following commands to install Python and MySQL :
$ sudo apt update
$ sudo apt install python-dev libssl-dev
$ sudo apt install mysql-server-5.7
If the apt command isn't present, you're probably using a slightly older version of
Linux, where you should use apt-get instead.
It’s a good idea to start the MySQL server, or you won’t be able to log in or make
changes to it :
$ systemctl start mysql
$ systemctl enable mysql
You can verify the version of Python and MySQL by running
$ python --version
$ mysql --version
You should see Python 3.x.x and mysql ... 5.7.x installed.
Editing Code
You should use the code editor, or integrated development environment, that you’re
most comfortable with. We recommend you use something like Visual Studio Code, as it
contains just enough automated tooling to be useful, but is still fast and free.
You can download it at https://code.visualstudio.com .
as you open Masonite files, you’ll see prompts to install Code extensions. these
will give you handy tips and tell you when you have an error in your code. We
recommend you install these when prompted.
Cha Pter 1 Gett InG Started

7
Setting Up in Other Environments
If you’re using macOS or Linux, these instructions should be fine for you. If you’re using
a different version of Linux, or Windows, you will probably need to follow a different
guide for installing Python on your system.
A good place to look is the official Masonite documentation: https://docs.
masoniteproject.com .
If you’re looking for a refresher of the Python language, check out www.apress.com/
la/book/9781484200292 .
Creating a New Masonite Application
One of the tools Masonite provides, to help with project creation and maintenance, is
a global command-line utility. Along with Python, the preceding instructions should
also have installed a dependency management tool called Pip. We can install Masonite’s
command-line utility, using Pip:
pip install --user masonite-cli
depending on how your system is set up, you may have to use a binary called
pip3 instead. If you’re unsure which to use, run which pip and which pip3 .
these will give you hints as to where the binaries are installed, and you can pick
whichever binary looks better to you.
After this command has finished executing, you should have access to Masonite’s
command-line utility, craft . You can verify this by inspecting its version:
craft --version
Now, it’s time to create a new Masonite project. Navigate to where you’d like the
project’s code folder to reside, and run the new command:
craft new friday-server
You can substitute your own project name where you see friday-server . I’ve called
mine that, for reasons that will become obvious in a bit.
Cha Pter 1 Gett InG Started

8
You should then be prompted to navigate into the newly created folder and run an
install command. Let’s do that :
cd friday-server
craft install
This command installs the dependencies Masonite needs to run. After running this
code, you should see some text telling you “key added to your .env file”.
To make sure everything is working, let’s run the serve command:
craft serve
This should tell you that the application is being served at “ http://127.0.0.1:8000 ”,
or something similar. Open that URL in your browser, and you should be greeted with
the Masonite 2.1 landing page as shown in Figure  1-1 .
Figure 1-1. The Masonite 2.1 landing page
Cha Pter 1 Gett InG Started

9
Exploring the Masonite Folder Structure
We’re going to be spending a lot of time in this codebase, so a basic understanding of the
folder structure (as shown in Figure  1-2 ) will be beneficial to us knowing where to create
new files and change existing ones.
Figure 1-2. The Masonite 2.1 folder structure
Cha Pter 1 Gett InG Started

10
Let’s look at what each of these files and folders are for, without going into too much
detail:
1. app  – This folder starts off holding the parts of the application that
respond to individual HTTP requests and apply blanket rules and
restrictions to all requests and responses. We’ll be adding lots
to this folder, as we add ways for the application to respond to
requests.
2. bootstrap  – This folder hold scripts used to start the application
and cache files generated during the running of the application.
3. config  – This folder holds configuration files, which tell the
Masonite application which settings to use while running.
4. databases  – This folder holds database configuration scripts.
Unlike the config folder’s scripts, these are meant to modify an
existing database, creating and modifying tables and records.
5. resources  – This folder holds static files, like HTML templates.
6. routes  – This folder holds files which map HTTP requests to
the parts of the app folder which are meant to handle them. It’s
where we tell the application how to get from browser URLs to
application files.
7. storage  – This folder holds more static files, but generally they’re
the kind that we’ll be putting there ourselves. Things like file
uploads, Sass files, and publicly accessible files (like favicon.ico
and robots.txt ).
8. tests  – This folder holds testing scripts, which we will write to
make sure our application functions as intended.
9. .env  – This file stores environment variables. These variables
are likely to change between environments and are often secret
values (like service keys). This file should never be committed to
shared code storage locations, like GitHub. That’s why the default
.gitignore file specifically ignores .env .
Cha Pter 1 Gett InG Started

11
there are other files which are generally found in Python applications. When
appropriate, we’ll talk about these files.
As we build our example application, we’ll start to add files and change these
existing files. When you see paths to files, you can assume that we’re talking about them
as relative to the base folder.
Planning the Example Application
Some people find it easier to learn a tool when they’re using that tool to build something.
For that reason, in this book, we’re going to build an example application.
It’s not essential that you follow along with the example, since the main focus of
the book is the theoretical and technical usage of Masonite libraries. It’s merely
additional to what you’ll learn about Masonite, as a means of solidifying your
learning.
I love experimenting with electronics, and this excitement has only grown after
seeing movies like Iron Man . In Iron Man the viewer gets to know a man named Tony
Stark, who has built mansions full of tech to automate every aspect of his life.
After watching those movies, I had a strong urge to do the same.
When we were planning this book, we were trying to think of interesting topics for
an example project, and this idea came to mind. So, we’re going to build our example
project with the goal of automating parts of our lives.
We’ll begin with simple tasks, like implementing podcast and media center
management, and continue on to larger things like getting up-to-date weather reports
and automatically responding to emails.
If we have time, we’ll even delve into the world of electronics, connecting devices to
our code and having them perform physical tasks for us.
Following the trend of the movies, I want to call my home automation and
personal assistant persona Friday. I am beyond excited at the potential of this example
application, and I hope you find it as stimulating to your learning as we intend for it to be.
Cha Pter 1 Gett InG Started

12
Summary
In this chapter, we took the first steps toward learning about Masonite. We installed
some tools and created a new application. We’re going to build on this application
through the rest of the book.
We also discussed the theme of this example application. As we continue, feel free to
add your own design and flair to the example. It’s meant to keep you interested in using
Masonite, and hopefully becomes something useful to you by the end of the book.
Cha Pter 1 Gett InG Started

13 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_2
CHAPTER 2
Routing
In the previous chapter, we set things up to be able to start building a Masonite
application. We installed Python, a database, and the craft command-line utility.
In this chapter, we’re going to learn about the process of connecting browser URLs to the
application code that handles each request. We’ll learn about the different types of requests
Masonite can interpret, and we’ll start to build functionality for our example application.
“How Does Masonite Handle Requests?”
The Internet is built around the idea of request/response cycles. The same few things
happen, whenever you open a browser and enter a URL :
1. Your browser connects the address you type in (like www.apress.com )
with an IP address. IP addresses come in two forms: IPv4 and IPv6.
Both of these are types of addresses that are meant to connect
different machines together, but are not easy for humans to deal with.
Things called Domain Name Servers (or DNS for short) have
lookup tables which take in the human-readable domain names
and give out the IPv4 or IPv6 address back to the browser.
2. The browser makes a request to the server, at the end of the IPv4
or IPv6 address (and usually at port 80 or port 443). After DNS
resolution, a request to www.apress.com will result in the browser
sending a request to 151.101.172.250:443 (the address might be
different when you try it, since servers can change their IP addresses).
3. The server then has a chance to interpret the request and respond
accordingly. Most of the time, the response will be a textual body
(which can contain HTML) and some headers describing the
server and response body.

14
It’s the third step where Masonite takes over. Masonite applications listen on port 80
and port 443, unless otherwise configured, and are given the HTTP request to interpret.
HTTP means Hypertext Transfer Protocol, and it describes the format in which
requests are made and responses are sent. There’s a huge amount of detail I’m
glossing over, because it’s largely unimportant to what we’re learning about
Masonite. If you’d like to see the full specification, you can find it at
https://tools.ietf.org/html/rfc2616 .
Masonite takes in an HTTP request, performs some initial formatting on it, and
passes that request to a route handler. In order for us to respond to specific requests, we
need to create route handlers and the accompanying controllers.
Creating Controllers and Routes
This code can be found at https://github.com/assertchris/friday-
server/tree/chapter-2 .
In Masonite, we think of routes as the first point of entry into the application, but before
we can create new routes, we have to create new controllers.
The craft command has built-in functionality to help us create new controllers with
little effort. Inside our project folder, we can use the following command:
craft controller Home
This will create a file, called HomeController.py , in the app/http/controllers
folder. Controllers are the glue between HTTP requests and responses. The one we just
made looks like this:
"""A HomeController Module."""
from masonite.request import Request
from masonite.view import View
class HomeController:
"""HomeController Controller Class."""
CHaPTer 2 rou TIng

15
def __init__ (self, request: Request):
"""HomeController Initializer
Arguments:
request {masonite.request.Request}...
"""
self.request = request
def show(self, view: View):
pass
This is from app/http/controllers/HomeController.py .
Controllers are ordinary Python classes. What makes them powerful is that they are
created and called using a dependency injection container. We’ll dive deeply into what
that means, in Chapter 3.
For now, all you need to know is that the Request and View objects you see, there,
will automatically be provided. We don’t need to create new instances of this controller,
and feed it with those objects, in order for it to function.
Most of the controller code is documentation. For the sake of brevity, we’re going
to omit as much of this kind of documentation as possible. You’ll see it in your files
(and it’ll still be in ours), but we won’t be repeating it in code listings.
Now that we’ve made a controller, we can connect it to a route. If you open routes/
web.py , you’ll notice it already has a defined route. You’ve probably also noticed the
existing controller. Forget about those, for a minute. Let’s add our own route, to respond
to GET requests at /home :
from masonite.routes import Get, Post
ROUTES = [
# ...
Get().route('/home', 'HomeController@show').name('home'),
]
CHaPTer 2 rou TIng

16
This is from routes/web.py .
This should be enough, right? Let’s start the server up:
craft serve
older versions of Masonite require a -r flag to make the server restart every time
it sees a file change. If your updates aren't showing in the browser, make sure the
server is reloading every time a file changes, by checking in the console. If you're
not seeing any activity, there, you may need this flag.
When we open the server in a browser (at http://127.0.0.1:8000/home ), we see the
screen shown in Figure  2-1 .
Figure 2-1. Oops! An error
CHaPTer 2 rou TIng

17
That can’t be normal, can it? Well, let’s go back to the controller code:
from masonite.request import Request
from masonite.view import View
class HomeController:
def __init__ (self, request: Request):
self.request = request
def show(self, view: View):
pass
This is from app/http/controllers/HomeController.py .
Our route tells Masonite to use the show method, but the show method just passes.
For routes to work, they need to return something. The error message tells us this, albeit
in a roundabout way: “Responses cannot be of type: None.”
The fix is surprisingly simple. We just need to return something form the show
method. A plain string will do:
def show(self, view: View):
return 'hello world'
This is from app/http/controllers/HomeController.py .
CHaPTer 2 rou TIng

18
Success! This may not seem like much, but it’s the first step toward building a
functional application.
Let’s review what has happened so far:
1. We opened a browser to http://127.0.0.1:8000/home . The
browser created an HTTP request and sent it to that address.
2. The Masonite server, started with craft serve -r and listening
on port 80, received the HTTP request.
3. The Masonite server looked for a route matching /home , with
the GET request method. It found a match and looked at which
controller and method to use.
4. The Masonite server fetched the prevailing Request and View
objects, instantiated the controller, and sent those objects to the
controller and show methods.
5. We told the controller to return a string, for that type of request,
which it did. That string was formatted as an HTTP response and
sent back to the browser.
6. The browser displayed the HTTP response.
Figure 2-2. Returning a string from show
CHaPTer 2 rou TIng

19
every route you create will be connected with a method in a controller, or directly
to a response file. You’ll need to follow this process often, so it’s important that you
get the hang of it now.
This is just an ordinary GET route, but there are many different kinds of routes and
variations we can use.
Creating Different Kinds of Routes
We’ve kinda glossed over this, but HTTP requests can have different aspects that
separate them. We’ve already seen what GET requests look like – the kind that happen
when you type and address into your browser.
Different Methods
There are a few other methods:
• POST  – These kinds of requests usually happen when you submit a
form in your browser. They are used to signify that the object(s) being
conveyed on should be created on the server.
• PATCH , PUT  – These kinds of requests usually don’t happen in the
browser, but they have special meaning and operate similarly to POST
requests. They are used to signify that the object(s) being conveyed
on should be partially changed or overwritten, respectively.
• DELETE  – These kinds of requests also don’t usually happen in the
browser, but they operate similarly to GET requests. They are used to
signify that the object(s) being conveyed should be removed from the
server.
• HEAD  – These kinds of requests do happen in the browser, but they are
more about the metadata of the object(s) being conveyed than they
are about the object(s) themselves. HEAD requests are ways to inspect
the object(s) in question and whether the browser has permission to
operate on them.
CHaPTer 2 rou TIng

20
Using these request methods, requests to the same path (like /room ) can mean
different things. A GET request can mean that the browser, or person using it, wants to see
information about a specific room.
A POST , PATCH , or PUT request may indicate that the user wants to make or change a
room, specifying attributes to create or change it with.
A DELETE request may indicate the user wants the room removed from the system.
Different Parameters
Routes (and requests) can also have different kinds of parameters. The first, and easiest
to think about, is the kind of parameter that is part of the URL.
You know when you see blog posts, with URLs like https://assertchris.io/
post/2019-02-11-making-a-development-app ..? That last part of the URL is a
parameter, which could be hard coded or could be dynamic, depending on the
application.
We can define these kinds of parameters by changing how the route looks:
from masonite.routes import Get, Post
ROUTES = [
# ...
Get().route('/home/@name', 'HomeController@show')
.name('home'),
]
This is from routes/web.py .
Notice how we’ve added /@name to the route? This means we can use URLs like /
home/ chris , and that chris will be mapped to @id . We can access these parameters in
the controller:
def __init__ (self, request: Request):
self.request = request
def show(self, view: View):
return 'hello ' + self.request.param('name')
CHaPTer 2 rou TIng

21
This is from app/http/controllers/HomeController.py .
The __ init__ method (or the constructor) accepts a Request object, which we can
access inside the show method. We can call the param method, to get the named URL
parameter, which we defined in the route.
Since we only have the show method, and all __ init__ is doing is storing the Request
object, we can shorten this code:
from masonite.request import Request
from masonite.view import View
class HomeController:
def show(self, view: View, request: Request):
return 'hello ' + request.param('name')
This is from app/http/controllers/HomeController.py .
This works, as before, because controller methods are called after their dependencies
have been resolved from the dependency injection container.
If you’re using a dependency in a single method, you should probably just accept
that parameter in the same method. If you’re reusing it multiple times, it’s a bit
quicker to accept the dependency in the constructor.
Another way to parameterize a request is to allow query string parameters. This is when
a URL is requested, but ends in syntax resembling ?name=chris . Let’s make the @name part of
the route optional and allow for it to be given as a query string parameter instead:
from masonite.routes import Get, Post
ROUTES = [
# ...
Get().route('/home/@name', 'HomeController@show')
.name('home-with-name'), Get().route('/home', 'HomeController@show')
.name('home-without-name'),
]
CHaPTer 2 rou TIng

22
This is from routes/web.py .
The quickest and easiest way to make a parameter optional is to define a second
route that doesn’t require it to be provided. Then, we have to modify the controller to
work with both:
from masonite.request import Request
from masonite.view import View
class HomeController:
def show(self, view: View, request: Request):
return 'hello ' + (
request.param('name') or request.input('name')
)
This is from app/http/controllers/HomeController.py .
We can access query string parameters using the input method, on the Request
object. Wanna know the best part about this code? If we want to respond to POST , PATCH ,
or PUT requests, we don’t need to change any of this controller code.
We can modify the /home routes to accept GET and POST requests:
from masonite.routes import Get, Post, Match
ROUTES = [
# ...
Match(['GET', 'POST'], '/home/@name',
'HomeController@show').name('home-with-name'),
Match(['GET', 'POST'], '/home',
'HomeController@show').name('home-without-name'),
]
This is from routes/web.py .
CHaPTer 2 rou TIng

23
We have to allow insecure POST requests to these URLs, in the CSRF middleware:
from masonite.middleware import CsrfMiddleware as Middleware
class CsrfMiddleware(Middleware):
exempt = [
'/home',
'/home/@name',
]
every_request = False
token_length = 30
This is from app/http/middlware/CsrfMiddleware.py .
We’re going to be learning about middleware in Chapter 8 and CSRF protection in
Chapter 4. For now, it’s enough to know that POST requests are usually prevented when
they come from outside the application.
Browser requests should continue to work, but now we can also make POST requests
to these endpoints. The easiest way to test this is to install an app called Postman. Here
are the steps for testing:
1. Go to www.getpostman.com and download and install the app.
You’ll need to create a free account when you open the app, unless
you’ve used Postman before.
2. Change the method dropdown from Get to Post and enter the
URL httsp:// 127.0.0.1:8000/home .
3. Change the data tab from Params to Body and enter name
(key) = chris (value).
4. Click Send .
CHaPTer 2 rou TIng

24
If either the GET or POST request gives you an error, along the lines of “can only
concatenate str (not “bool”) to str,” it’s probably because you’re providing neither the
route param nor the query string/post body name.
Route Groups
Sometimes you want to configure multiple routes to be similarly named, or to behave in
similar ways. We can simplify the /home routes by grouping them together:
from masonite.routes import Get, Match, RouteGroup
ROUTES = [
# ...
RouteGroup(
[
Match(['GET', 'POST'], '/@name',
'HomeController@show').name('with-name'),
Match(['GET', 'POST'], '/',
Figure 2-3. Sending POST requests to the server
CHaPTer 2 rou TIng

25
'HomeController@show').name('without-name'),
],
prefix='/home',
name='home-',
)
]
This is from routes/web.py .
If we use RouteGroup instead of Match or Get , we can define common path and name
prefixes. This saves a lot of typing and makes it easier to see routes that have something
in common.
There are a few more advanced aspects of RouteGroup , but they are best left
for the chapters where they are properly explained. Look out for middleware in
Chapter 8 and domains (deploying) in Chapter 13 .
Exploring Request and Response
While we’re in the controller, let’s look at the Request and Response classes in a bit more
detail. We’ve already used a couple Request methods, but there are more to see.
We’ve seen how to request a single named input, but there’s also a way to get all
input for a request :
request.all()
This returns a dictionary of input. For HEAD , GET , and DELETE methods, this probably
means query string values. For POST , PATCH , and PUT methods, this probably means
request body data.
The latter methods can send their body data as URL-encoded values, or even as
JSON data.
CHaPTer 2 rou TIng

26
I say this “probably means” because the latter methods may also have query
string values. While this is allowed in most settings, it’s contrary to the HTTP
specification. When you’re designing your application to use the latter methods,
you should try to steer clear of mixing query strings and body data.
request.all() is useful, in situations where you’re unsure exactly what data you’re
expecting. There are variations of this method, which get a bit more specific:
request.only('name')
request.without('surname')
These methods limit the returned dictionary items and exclude the specified
dictionary items, respectively.
If you’re not sure what input you’re expecting, but you want to know of certain keys
are present, then there’s another method you can use:
request.has('name')
request.has() returns True or False , depending on whether or not the specified key
is defined. You could, for instance, alter the behavior of the route method, based on the
presence of certain bits of data. Perhaps you want to update a user’s account details, if
you detect data specific to that. Or, perhaps you need to reset their password, if you find
relevant data in their form submission. Up to you.
Reading and Writing Cookies
One of the ways we remember a user, and store data relevant to their session, is by
setting cookies. These can be set and read in the browser, so it’s important to realize that
the Masonite defaults protect against this.
Cookies can be set, using this method:
request.cookie('accepts-cookies', 'yes')
We won’t be able to read this, using JavaScript, unless we also disable HTTP-only and
server-side encryption:
request.cookie(
'accepts-cookies',
CHaPTer 2 rou TIng

27
'yes',
encrypt=False,
http_only=False,
expires='5 minutes',
)
This code also demonstrates how to set the expiry of cookies. By default, they will
expire the moment the user closes their browser, so any long-lived or persistent data
must have this value set.
Cookies can be read in a couple ways. The first is by specifying a key:
request.get_cookie('accepts-cookies', decrypt = False)
If you set Encrypt to False , then you need to set Decrypt to False . Otherwise the
Decrypt argument may be omitted. If Masonite tries to decrypt a cookie, but fails, then it
will delete that cookie. This is a security precaution against cookie tampering.
If you want to manually delete a cookie, you can do so with this method:
request.delete_cookie('accepts-cookies')
Sending Other Kinds of Responses
So far, we’ve only sent plain strings back to the browser. There are a myriad of other
responses we could be sending, beginning with JSON responses:
return response.json({'name': 'chris'})
This kind of response will have the appropriate content type and length headers
appended to the response. We can make it even shorter, by returning a dictionary:
return {'name': 'chris'}
It’s this kind of magic that makes me enjoy Masonite so! There’s something similar
happening when we return plain strings, but this is the first time we’re digging deep
enough to know that is what’s happening.
Now, imagine we wanted to redirect the user, instead of returning some renderable
response to the browser. We can use the redirect method for that :
return response.redirect('/home/joe')
CHaPTer 2 rou TIng

28
This isn’t too flexible on its own. We can, however, use a similarly named Request
method, to redirect to a named route:
return request.redirect_to(
'home-with-name',
{'name': 'chris'},
)
This is one of the main reasons I recommend you always name your routes. If you
later want to change the path to the route, all the code that references a named
route will continue to function unmodified. It’s also often quicker using a named
route than it is reconstructing or hard coding the ur L you need.
Creating Views
The final kind of response I want to talk about is the kind that involves HTML. If we’re
interested in building a rich UI, plain strings just aren’t going to cut it. We need a way
to construct more complex templates, so we can show dynamic and styled interface
elements.
Let’s see what it would look like if we make the /home routes show dynamic
HTML. The first step is to create a layout file:






@block content

@endblock


CHaPTer 2 rou TIng

29
This is from resources/templates/layout.html .
It’s a good idea to build our templates to fit inside a layout, so that global changes
can be applied in one place. We’ll see this come into play, shortly. For now, let’s create a
home template:
@extends 'layout.html'
@block content

hello {{ name }}


@endblock
This is from resources/templates/home.html .
Notice how little this template has to repeat, since we’re extending the layout.html
template. This path is relative to the templates folder. Blocks defined in “outer” templates
can be overridden by “inner” templates. This means we can define default content,
which “inner” templates can replace with more specific content.
Masonite views use a superset of the Jinja2 template syntax, which can be found
at http://jinja.pocoo.org/docs . one important difference is that Masonite
templates can use @extends syntax in place of {%extends ...%} syntax.
There are a couple things we need to change, in the controller, to use these
templates. Firstly, we’re using dynamic data, in the form of {{ name }} . This data needs
to be passed into the view. Secondly, we need to specify which view template to load.
Here’s how that code looks:
def show(self, view: View, request: Request):
return view.render('home', {
'name': request.param('name') or request.input('name')
})
This is from app/http/controllers/HomeController.py .
CHaPTer 2 rou TIng

30
We pass the name data, to the view, by defining a dictionary of dynamic data.
There’s a lot more to learn about the Jinja2 syntax and how Masonite extends it.
We’ll explore it more as we build our example application.
Starting the Example Application
Before we begin, I want to stress that the example application is entirely optional to
your learning. each chapter’s example code can be found on gitHub, so you don’t
have to retype anything.
That said, we highly recommend that you at least follow along with the
development of the example application. We believe you’ll find it easier to
remember what you learn if you see it built into something real. More so if you
build something real yourself.
This code can be found at https://github.com/assertchris/friday-
server/tree/between-chapters-2-and-3 .
I listen to many podcasts, so I’d like Friday (my personal assistant and home
automation software) to organize and play podcasts on demand. Friday will begin her
life as a glorified podcast app.
Let’s start by creating a page through which to search for new podcasts. We need a
new controller and template:
craft controller Podcast
craft view podcasts/search
This new controller is exactly the same as the HomeController we created, except in
name. We should rename the show method, so it more accurately reflects what we want
to show:
from masonite.view import View
class PodcastController:
def show_search(self, view: View):
return view.render('podcasts.search')
CHaPTer 2 rou TIng

31
This is from app/http/controllers/PodcastController.py .
This new view is just an empty file, but it’s in the right location. Let’s give it some
markup, so we can tell whether or not it’s being correctly rendered:
@extends 'layout.html'
@block content

Podcast search


@endblock
This is from resources/templates/podcasts/search.html .
Before this will work, we need to add a route. We can start with a RouteGroup ,
because we expect to add more routes, with similar names and prefixes.
from masonite.routes import Get, Match, RouteGroup
ROUTES = [
# ...
RouteGroup(
[
Get().route('/', 'PodcastController@show_search')
.name('-show-search')
],
prefix='/podcasts',
name='podcasts',
),
]
This is from routes/web.py .
CHaPTer 2 rou TIng

32
If you’re running the craft serve -r command, you only need to go to /podcasts
in the browser to see this new page. It looks a bit ugly, so I think we should start applying
some styles. Let’s use a tool called Tailwind ( https://tailwindcss.com ), since it’s easy
to get started with:
npm init -y
npm install tailwindcss --save-dev
This adds two new files and one new folder. You can commit the files to git, but I
recommend adding the folder (which is node_modules ) to your .gitignore file.
You can always recreate it by running npm install .
Masonite provides an easy way to build Sass ( https://sass-lang.com ) for our
application. We can add the following link to our layout file:







@block content

@endblock


This is from resources/templates/layout.html .
This /static/style.css file doesn’t exist, but that’s because it is being redirected
to storage/compiled/style.css . This file is generated from what we put into storage/
static/sass/style.css . We can add new styles, to that file, and see them reflected in
our application:
CHaPTer 2 rou TIng

33
@import "node_modules/tailwindcss/dist/base";
@import "node_modules/tailwindcss/dist/components";
@import "node_modules/tailwindcss/dist/utilities";
h1 {
@extend .text-xl;
@extend .font-normal;
@extend .text-red-500;
}
input {
@extend .outline-none;
@extend .focus\:shadow-md;
@extend .px-2;
@extend .py-1;
@extend .border-b-2;
@extend .border-red-500;
@extend .bg-transparent;
&[type="button"], &[type="submit"] {
@extend .bg-red-500;
@extend .text-white;
}
}
This is from storage/static/sass/style.scss .
This will only work if we’ve installed the Sass library, using pip install
libsass or pip3 install libsass . You may, also, not see changes just by
refreshing the page. If you aren’t seeing changes, restart the server and clear your
browser cache.
I don’t want to go into too much detail about Tailwind, except to say that it is a utility-
based CSS framework. That means styles are applied by giving elements classes (inline),
or extracting classes in the way we’ve done with these h1 and input selectors.
Let’s also reposition the content so that it sits in the middle of the page:
CHaPTer 2 rou TIng

34








@block content

@endblock



This is from resources/templates/layout.html .
Let’s also add a search form and some dummy results:
@extends 'layout.html'
@block content

Podcast search












Title

Description



CHaPTer 2 rou TIng

35



Author


date










Title

Description






Author


date










Title

Description






CHaPTer 2 rou TIng

36
Summary
In this chapter, we learned about controllers, routing, and views. We created multiple
entry points into our application, accepted multiple request methods, and responded
with simple and complex responses.
We also began work on our personal assistant application, got Sass up and running,
and started applying styles to custom markup.
In the next chapter, we’re going to learn about some of the more advanced tools
Masonite provides, beginning with the dependency injection container.

Author


date







@endblock
This is from resources/templates/podcasts/search.html .
Figure 2-4. Podcast search form and results
CHaPTer 2 rou TIng

37 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_3
CHAPTER 3
The Service Container
Masonite is built around what is called a “service container.” Don’t let this wording confuse
you though. All a service container is just a group of… services, exactly! Services in this
context are just features. Think of the service container as a toolbox, services as your tools,
and Masonite as your workshop. A service can be as small as a Mail class for sending mail
or a Queue class for sending jobs to a message broker like RabbitMQ. Services can even get
more advanced like a routing engine to map the URL to a given controller.
All of these services are loaded (bound) into the service container, and then we fetch
the service at a later time. More on why this is important later on.
The real benefit of a service container is that it handles application dependencies for
you. Take the example of all the time you had to:
• Im port an object.
• Initialize the object.
• Pass some data to a setter method.
• Finally call the object methods.
• Do the same thing in several files.
Masonite’s service container is also called an IoC container. Service container
and IoC container will be used interchangeably. IoC stands for Inversion of Control.
Inversion of Control simply means that the conventional control of objects is flipped.
When normally an object is responsible for
• Finding the object
• Instantiating the object
with Masonite’s IoC container, all objects are
• Instantiated
• Handed to the object

38
See how the control has inverted? The benefit of building an application that is
wrapped around a service container is actually really simple. There are two major
benefits the container has.
The first benefit is that it allows you to load all of your services (objects) into the
container at the beginning of when your application boots up (like starting the server)
which is then used throughout your entire application. This removes the need to have
to instantiate a class in multiple places. It also allows you to swap that class out with any
other class at a later time. Maybe you don’t like the logging class you are using so you
swap it out for another implementation.
The second benefit is that it allows you to link most of your classes together that
depend on each other. For example, if a Logger class needs the Request and Mail class,
Masonite will wire them all together and give you the completed and initialized class
ready to use. There is no need to wire all your application dependencies together. This
saves a lot of time is invaluable to a maintainable codebase.
Let’s get started on a bit more about the container.
The Problem We Are Solving
Take this as an example. We have two very simple classes.
The first class sends a simple email from the request object and logs a message
saying that the email is sent :
from some.package import SMTPMail, BaseMail
class Mail(BaseMail):
def __init__ (self, request, logger):
self.request = request
self.logger = logger
def send(self, message):
self.to(self.request.input('email')).smtp(message)
Simple enough, right? We may use this inside a controller method like this:
from masonite.request import Request
Chapter 3 the Servi Ce Container

39
from app.mail import Mail
from app.logger import Logger
class MailController:
def show(self, request: Request):
logger = Logger(level='warning', dir='/logs')
mail = Mail(request, logger)
mail.send('Email has been sent!')
This code may look fine, but notice how we had to set up a new object called logger
just to pass that information to the mail class. Imagine for a second that we had to use
this class in ten different files. Maybe 20 other objects had to use this Logger class. Are
we really going to import it every time into the file, initialize it, and pass it in?
Type Hinting
Now notice we have a line in the preceding method signature that looked like this:
def show(self, request: Request):
This is called “type hinting” and it is the basis for how we will primarily be
interacting with the service container.
Type hinting is the art of telling a parameter which type it should be. We may tell a
parameter to be a Request class or a Logger class.
On the Masonite side, Masonite will say “oh this parameter wants to become a
Logger class. I already know about that logger class so I will force that parameter to be
the same object I already know about.”
A type hint is semantically written like this:
from some.package import Logger
def function(logger: Logger):
pass
The syntax is {variable}: Class .
The variable can be named whatever you like. For example, the signature can be
written any of these ways:
Chapter 3 the Servi Ce Container

40
def function(log: Logger):
def function(logging: Logger):
def function(l: Logger):
The variable is simply just that, a variable. Name it whatever you like.
There are several places throughout the codebase where Masonite will inspect
objects before calling them. These places are things like controllers, middleware, and
queue job methods. These are just places that Masonite resolves for you, but you can
always resolve your own classes as well.
Service Providers
Now you may be wondering how the heck does Masonite know which class to provide? I
asked for the Logger class and I got the Logger class. So how does Masonite know which
class to provide?
This is all done by what Masonite calls “service providers.”
Service providers are simple classes that are used to inject services into the
container. They are the building blocks that make up a Masonite application. Masonite
checks its service provider list and uses that to bootstrap the application. Masonite is
actually made up primarily of these service providers.
This is an example of a service provider list :
from masonite.providers import AppProvider, SessionProvider, ...
PROVIDERS = [
# Framework Providers
AppProvider,
SessionProvider,
RouteProvider,
StatusCodeProvider,
WhitenoiseProvider,
ViewProvider, HelpersProvider,
]
Chapter 3 the Servi Ce Container

41
This is the simple flow of the core of a Masonite application:
1. WSGI server (like Gunicorn) first starts up.
2. Masonite runs through the list of service providers and runs the
register method on all of them.
3. Masonite runs back through and runs all the boot methods on all
service providers where wsgi = False . This wsgi = False attribute
just tells Masonite that we don’t need a WSGI server to be running
to bootstrap this portion of the application. If wsgi = True , then
Masonite will run the boot method on every single request. If we
have a service provider that loads a Mail service in the container, it
does not need to be run on every request.
4. Masonite will then listen on a specific port for any requests.
5. When a request hits the server (like the homepage), then the
Masonite will run the boot method on only the service providers
where wsgi = True or the attribute does not exist (it’s True by
default). These are providers that need to be run like mapping
a request URL to a route and controller or loading the WSGI
environment into the request object.
You can tell by the preceding bullet points that Masonite is entirely dependent on
this service container. If you need to swap out the functionality of Masonite, then you
swap out the service container.
In the flow of things, you will build out classes that perform a specific service (like
logging) and then use a service provider to put it into any Masonite application.
A simple service provider will look like this:
from masonite.providers import ServiceProvider
class SomeServiceProvider(ServiceProvider):
def register(self):
pass
def boot(self):
pass
Chapter 3 the Servi Ce Container

42
The Register Method
Let’s break down the service provider a bit more because if you know how this works,
then you are on your way to writing extremely maintainable code within your Masonite
application.
The register method is first to run on all service providers and is the best place to
bind your classes into the container (more on binding in a bit). You should never try to
fetch anything from the container inside the register method. This should be reserved
only for putting classes and objects in.
We can register classes and objects by using the bind method. Binding is the concept
of putting an object into the container.
from some.package import Logger
..
def register(self):
self.app.bind('Logger', Logger(level='warning', dir='/logs'))
We also want Masonite to manage the application dependencies for our new Mail
class:
from some.package import Logger, Mail
..
def register(self):
self.app.bind('Logger', Logger(level='warning', dir='/logs'))
self.app.bind('Mail', Mail)
The classes have now been put into the container. Now we can do what’s called “type
hinting” it into our mail class.
from some.package import Logger
class Mail:
def __init__ (self, logger: Logger):
self.logger = logger
Chapter 3 the Servi Ce Container

43
Now when Masonite attempts to construct this class, it will go to initialize the class
but say “hey I see you want a Logger class. Well, I have that logger class already. Let me
give you the one I know about which is already set up inside my container.”
Now when we resolve this Mail class, it will act something like this:
from some.place import Mail
mail = container.resolve(Mail)
mail.logger #==
We’ll talk more about resolving in a bit, so don’t let this part confuse you yet. Now
notice the Logger class Masonite knew about was passed into the Mail class for us
because we type hinted it.
The Boot Method
The boot method is where you will do most of your interaction with the container. Here
is where you will do things like construct classes, tweak classes already in the container,
and specify container hooks.
A typical service provider that adds mail functionality will look like this:
from some.place import MailSmtpDriver, Mail
class MailProvider(ServiceProvider):
wsgi = False
def register(self):
self.app.bind('MailSmtpDriver', MailSmtpDriver)
self.app.bind('Mail', Mail)
def boot(self, mail: Mail):
self.app.bind('Mail', mail.driver('smtp'))
So what we did here was we bound a MailSmtpDriver as well as the full Mail
class into the container when it is registered. Then after all providers are registered,
we resolved the Mail class back out of the container and then bound it back into the
container but with the driver set as smtp .
Chapter 3 the Servi Ce Container

44
This is because there could be other service providers that register additional mail
drivers into the container so we want to interact with the container only after everything
has been registered.
The WSGI Attribute
You will notice that there is a wsgi attribute that is either set to True or False . Showing
only the first half of the class, it looks like this:
class MailProvider(ServiceProvider):
wsgi = False
def register(self):
If this attribute is either missing or set to True (it is True by default), then it will run
on every single request. But we see here that we are only adding a new mail feature, so
we really don’t need it to run on every request.
Nearly all service providers will not need to run on every request. Service providers
that need to run on every request are primarily those vital to the framework itself like the
“RouteProvider ” which takes the incoming request and maps it to the correct route.
Something important as well on these providers you may see is a “ wsgi = True ”
parameter. This property will be used to indicate that specific providers should run on
every request. If you need to run code based on a CSRF token of the user, that could
change between requests, so you will need to set the attribute to True . You should find
that most application level service providers simply need to bind more classes into the
service container so this attribute is usually set to False.
Another provider that runs on every request is the StatusCodeProvider which will
take a bad request (e.g., 404 or 500 ) and show a generic view during production.
But now that we have a provider that simply binds a few classes to the container and
we don’t need anything related to the request, we can make sure that wsgi is False .
The only downside to not doing this is that it will just take some extra time on the
request to execute code that really doesn’t need to execute.
Chapter 3 the Servi Ce Container

45
More on Binding
There are some important things to note when it comes to the bind method. There
are basically two types of objects we can bind into the container which are classes and
initialized objects. So let’s go through what these two types of objects are.
Class vs. Object Behavior
Classes are simple uninitialized objects, so looking back to our previous Mail class, we
have this example:
from some.place import Mail
mail = Mail # this is a class
mail = Mail() # this is an uninitialized object
This is important because you can have several different objects. If you modify one
object, it does not modify the other object. Take this as an example:
from some.place import Mail
mail1 = Mail()
mail2 = Mail()
mail1.to = 'user@email.com'
mail2.to #== '' empty
mail2.to = 'admin@email.com'
So because of this behavior, we can bind a class into the container and not an
initialized object :
from some.place import Mail
container.bind('Mail', Mail)
Now every time we resolve it, it will be different because it is constructed each time:
from some.place import Mail
container.bind('Mail', Mail)
Chapter 3 the Servi Ce Container

46
mail1 = container.resolve(Mail)
mail2 = container.resolve(Mail)
mail1.to = 'user@email.com'
mail2.to #== '' empty
mail2.to = 'admin@email.com'
Now that we know this behavior, we can also bind initialized objects into the
container. This will be the same object no matter how many times we resolve it. See
this example now:
from some.place import Mail
container.bind('Mail', Mail())
mail1 = container.resolve(Mail)
mail2 = container.resolve(Mail)
mail1.to = 'user@email.com'
mail2.to #== 'user@email.com'
Binding classes this way is useful because you can add new service providers which
can manipulate your objects for you. So adding a service provider may add a full session-
based feature to your request class. Since it’s the same object, any interaction with the
initialized class in the container will have the same functionality when we resolve it back
out later.
The downside, or upside depending on your use case, is that doing it this way
requires you to manually set up your classes since we need to construct the full object
before we bind it into the container.
So going back to our Logger and Mail example, we would have to do something like
this:
from some.place import Mail, Logger
container.bind('Mail', Mail(Logger()))
mail1 = container.resolve(Mail)
mail2 = container.resolve(Mail)
mail1.to = 'user@email.com'
mail2.to #== 'user@email.com'
Chapter 3 the Servi Ce Container

47
Not too big of a deal, but this is just a simple example.
In this case, we will call for the singleton pattern.
Binding Singletons
A singleton is a very simple concept. It just means that anytime we need this class, we
want the same exact class every time. Masonite achieves this simply by resolving the
class when we bind it . So the instantiated object goes into the container, and it will
always be that exact same object throughout the lifetime of the server.
We can bind singletons into the container by doing
from some.package import Logger, Mail
..
def register(self):
self.app.bind('Logger', Logger)
self.app.singleton('Mail', Mail)
Then whenever we resolve it, we will get the same object every time as well as the
Logger object. We can prove this by fetching it and checking the memory location:
mail1 = container.make('Mail')
id(mail1) #== 163527
id(mail1.logger) #== 123456
mail2 = container.make('Mail')
id(mail2) #== 163527
id(mail2.logger) #== 098765
Notice the Mail classes are the same, but the Logger classes are different.
Simple Binding
We have noticed that sometimes we are duplicating ourselves when we bind into the
container. Most of our binding keys are just the name of our classes. In order to get
around this, we can use the simple binding.
Chapter 3 the Servi Ce Container

48
# Instead of:
def register(self):
container.bind('Mail', Mail)
# We can do:
def register(self):
container.simple(Mail)
These two lines of code are exactly the same, and we can now make it as we normally
would use the class name as the key:
container.make('Mail')
Resolving Classes
So we have touched on briefly how to resolve an object with objects already in the
container, but let’s talk more about what resolving actually does.
Resolving simply means we will take the objects parameter list, extract what objects
are type hinted, find them in our container, and then inject them into the parameter list
and return the new object.
It’s important to note that an object we are resolving does not need to be in the
container, but all of the parameters do . So if we are resolving the Mail class we have
been working with, we don’t need to bind the Mail class into the container, but the
Logger class inside the Mail class initializer does. If it is not, then Masonite will throw an
exception since it cannot correctly build the object.
So a code example would look like this:
from some.place import Mail, Logger
container.bind('Logger', Logger)
mail = container.resolve(Mail)
mail.logger #==
Notice the Mail class is not in the container but its dependencies are. So we can
correctly build this object for you.
Chapter 3 the Servi Ce Container

49
Hooks
Hooks are another interesting concept. Hooks are useful when you want to intercept the
resolving, making, or binding of an object.
We can register callables with hooks by using one of three methods to register our
callable: on_make , on_bind , on_resolve .
On Make
An example would be something like this:
def change_name(obj):
obj.name = 'John'
return obj
...
def register(self):
self.app.on_make('Mail', change_name)
def boot(self):
mail = self.app.make('Mail')
mail.name #== 'John'
Notice that it fired this hook when we used the make method. This is because the
make method fires the on_make hook and looks for any registered hooks and passes the
object into it before returning it.
On Bind
Following the previous example, we can do the same thing when we bind the object into
the container:
def change_name(obj):
obj.name = 'John'
return obj
...
def register(self):
self.app.on_bind('Mail', change_name)
Chapter 3 the Servi Ce Container

50
mail = self.app.make('Mail')
mail.name #== 'John'
Notice it is doing the same thing as in the preceding example, but on the back end,
the hook is run when we bind the object.
On Resolve
The last hook is done every time we resolve the object :
from some.place import Mail, TerminalLogger
def change_logger(obj):
obj.logger = TerminalLogger()
return obj
...
def register(self):
self.app.bind('Mail', Mail)
mail = self.app.make('Mail')
mail.logger #==
def boot(self, mail: Mail):
mail.logger #== ''
Notice when we used the bind and make methods, our hook was never run. Not until
we resolved it was the logger changed. This is doubly useful for testing when you want to
modify some attributes on a class before it hits your test cases.
Swapping
Another awesome feature of the service container is the ability to swap out classes for
other classes. This is useful when you want to simplify your type hinting or want to code
to abstractions rather than concretions.
Here is a code snippet example:
from some.place import ComplexLogger, TerminalLogger, LogAdapter
Chapter 3 the Servi Ce Container

51
container.swap(
TerminalLogger,
ComplexLogger(LogAdapater).driver('terminal')
)
Now, whenever we resolve this TerminalLogger class, we will instead get back
whatever the more complex logger is:
from some.place import TerminalLogger
def get_logger(logger: TerminalLogger)
return logger
logger = container.resolve(get_logger)
logger #== ''
This is very good when it comes to building to abstractions rather than concrete
classes. We can swap out complex implementations with more complex ones in a nice
simple way.
Design Pattern Knowledge
In order to become an absolute expert on the service container, I think it is important to
grasp some knowledge first before continuing. This should give you enough well- rounded
knowledge to fully grasp everything. You might even need to reread this chapter a few
times to make everything click better if you are still unsure about anything.
Coding to Abstractions
A common design pattern in software design is the concept of dependency inversion.
Dependency inversion is a definition. All it means, in very simple terms, is that you want
to rely on an abstract class and not have to worry about the direct class itself. This is
useful when you need to change the lower-level classes out for something different down
the road.
Chapter 3 the Servi Ce Container

52
For example, if you are using a TerminalLogger , then you actually want to never
use the TerminalLogger class itself but instead want to use some abstraction of it
like a new LoggerConnection class. This class is called an abstract class because the
LoggerConnection could be anything. It could be a terminal logger, a Sentry logger, a file
logger, etc. It’s abstract because the class LoggerConnection is not actually clear what it
is using and therefore its implementation can be swapped out at any time later.
Dependency Injection
We have talked already about dependency injection and you might not have even
realized it. This is another 10 phrase fora 1 definition. All dependency injection is doing is
passing in a dependency into an object.
This can be as simple as passing in a variable to a function like this:
def take(dependency):
return dependency
inject_this = 1
x = take(inject_this)
That’s it! We have just done dependency injection. We took a dependency
(the “ inject_this ” variable) and gave it to (or injected it into) the take function.
Inversion of Control (IoC)
Ok, so this is the simple one. This is the same as the preceding dependency injection, but
it just depends on where the logic is happening. If the dependency injection is coming
from you, it’s just normal dependency injection, but if it is coming from the container or
framework, it is Inversion of Control.
This is why Masonite’s service container is sometimes referenced as an IoC
container.
This is any new information but just background knowledge that will allow you to
better understand what exactly the container is trying to achieve.
Chapter 3 the Servi Ce Container

53
Implementing Abstractions
Setting Up Our Abstraction
Let’s start by making our LoggerConnection class. We can code to abstractions by
making a simple base class which our concrete classes will inherit from:
class LoggerConnecton:
pass
Then we can build our terminal logger and inherit from our new LoggerConnection
class:
from some.place import LoggerConnection
class TerminalLogger(LoggerConnection):
def log(self, message):
print(message)
Now the last step is to bind the TerminalLogger into our container. We’ll do this in
our register method of one of our service providers:
from some.place import TerminalLogger
class LoggerServiceProvider:
def register(self):
self.app.bind('Logger', TerminalLogger)
Great! Now we are all set up to code to an abstraction. Remember our abstraction
was the LoggerConnection class, so now if we type hint that class, we will actually get our
TerminalLogger :
from some.place import LoggerConnection
class SomeController:
def show(self, logger: LoggerConnection):
logger #==
Chapter 3 the Servi Ce Container

54
So you may be wondering how it did this. How this works is that when Masonite is
trying to find the LoggerConnection class, it keeps track of any class that is a subclass
of LoggerConnection . Masonite will know that it does not have the LoggerConnection
in its container and will return the first instance of it instead. In this case, it is the
TerminalLogger .
Swapping Out Loggers
The biggest benefit of coding this way is that in the future you can switch out the logger
for a different logger and never touch any other part of the application. This is how you
build a maintainable codebase.
Take this for example. We want to now swap out our TerminalLogger for a new and
improved FileLogger .
First, we construct the class:
from some.place import LoggerConnection
class FileLogger(LoggerConnection):
def log(self, message):
# Write to log file
with open('logs/log.log', 'a') as fp:
fp.write(message)
And then we bind it to the container again but remove the previous binding:
from some.place import FileLogger
class LoggerServiceProvider:
def register(self):
# self.app.bind('Logger', TerminalLogger)
self.app.bind('Logger', FileLogger)
And that’s it! Now when we resolve it, we get the FileLogger :
from some.place import LoggerConnection
Chapter 3 the Servi Ce Container

55
class SomeController:
def show(self, logger: LoggerConnection):
logger #==
We didn’t change any other part of our application besides the container binding,
and it changed the type of logger everywhere else in the codebase.
Remembering
As you can tell, the resolve method needs to do a lot of things. It needs to inspect the
object to see what it is, it needs to extract out the parameter list, it needs to then inspect
each parameter one by one, it needs to loop through all the objects in the container,
actually find the correct one, and then build the list and inject it into the object for you.
As you can imagine, this is extremely expensive. Not only is it expensive, but it also
needs to sometimes run dozens of times per request.
Luckily though, Masonite’s container will remember what each object needs to be
resolved and caches them. Next time that object needs its dependencies again, like on
the next request, it will grab it from a special dictionary it builds and inject them for you.
This can lead to close to at least a 10x boost to your application. Testing shows that
resolving a class can go from 55ns per resolve down to 3.2ns per resolve when Masonite
remembers object signatures.
Collecting
Collecting is a really awesome feature where you can specify the objects you want from
the container, and it will return you a new dictionary of all the objects.
You can collect in two different ways: by key and by the object.
Collecting by Key
You can collect by the key by specifying a wildcard either before, during or after a key name.
Take this, for example, if you want to get all keys that end with Command :
container.bind('Request', Request())
container.bind('MigrateCommand', MigrateCommand)
Chapter 3 the Servi Ce Container

56
container.collect(' ∗Command')
#== {'MigrateCommand': MigrateCommand}
Notice we got all objects in the container bound the key via a wildcard of ∗Command .
This will get everything that ends with Command .
You can also work the other way and get everything that starts with a specific key:
container.bind('Request', Request())
container.bind('MigrateCommand', MigrateCommand)
container.collect('Migrate ∗')
#== {'MigrateCommand': MigrateCommand}
Notice these are the same since before we were getting everything that started with
the Command key and now we are getting everything that starts with the Migrate key. You
can also specify the wildcard in the middle of a key:
container.bind('Request', Request())
container.bind('SessionCookieDriver', SessionCookieDriver)
container.collect('Session ∗Driver')
#== {'SessionCookieDriver': SessionCookieDriver}
Collecting Session ∗Driver will get keys like SessionCookieDriver ,
SessionMemoryDriver or SessionRedisDriver .
This is really useful when you want to bind with a specific format, so you can easily
retrieve them again later.
Collecting Objects
You can also collect objects and subclasses of objects. Maybe you have a base class and
want to collect all instances of that base class. Masonite uses this for its scheduled tasks
package where all tasks inherit a base Task class and we can then collect all the tasks in
the container:
class Task1(BaseTask):
pass
class Task2(BaseTask):
pass
Chapter 3 the Servi Ce Container

57
container.simple(Task1)
container.simple(Task2)
container.collect(BaseTask)
#== {'Task1': Task1, 'Task2', Task2}
This is extremely useful if you want to bind objects into the container and then fetch
them back out using a parent class. If you are developing a package, then this is a very
useful feature.
Application
Ok, so now that you are an expert on the service container, let’s look into how we can use
all of our knowledge we have gained so far to add an RSS feed to our Friday app. We will:
• Add an RSS feed class into our container
• Add code to abstractions and not concretions
• Resolve the class from the container and use it with our controller
method
The Package
A great package to use for this is the feedparser package. So with our application and
inside our virtual environment, let’s install this package:
$ pip install feedparser
Let that install and now we’ll start building our abstraction class and our concretion
class.
Abstraction Class
Our abstraction class will be very simple. It’s basically going to be a base class which our
concrete class will inherit from.
Let’s call this class an RSSParser class. Inside this class, we’ll make a parse method
which will return the parsed RSS feed we will define on our concrete class.
Chapter 3 the Servi Ce Container

58
Let’s also create this class manually inside an app/helpers/RSSParser.py class:
# app/helpers/RSSParser.py
class RSSParser:
def parse(self, url):
pass
We called this RSSParser because we will be swapping this implementation with our
other RSS parsers in the future, so we needed to give it an abstract enough name where
we can do that.
Concrete Class
Since we are using the feedparser library, let’s call the concrete class the FeedRSSParser
class:
# app/helpers/FeedRSSParser.py
import feedparser
from .RSSParser import RSSParser
class FeedRSSParser(RSSParser):
def parse(self, url):
return feedparser.parse(url)
If that second import in the preceding code is confusing, it just means import the file
starting at the current directory. Since both files are in the app/helpers directory, we can
import it like this.
The Service Provider
Let’s create a new service provider which will be only responsible for handling our RSS
feed classes.
We’ll want to call it the RSSProvider since it provides RSS classes to our application.
We can use craft for this:
$ craft provider RSSProvider
Chapter 3 the Servi Ce Container

59
Once we do that, we can start binding our classes to the container like this:
from masonite.provider import ServiceProvider
from app.helpers.FeedRSSParser import FeedRSSParser
class RSSProvider(ServiceProvider):
wsgi = False
def register(self):
self.app.bind('FeedRSSParser', FeedRSSParser())
def boot(self):
"""Boots services required by the container """
pass
And lastly we need to tell Masonite about our provider, so let’s import it into config/
providers.py and add it to our list at the bottom:
We will import our provider at the top first and add it near the bottom of the list next
to the # Application Providers comment :
from app.providers.RSSProvider import RSSProvider
...
CsrfProvider,
HelpersProvider,
# Third Party Providers
# Application Providers
RSSProvider,
]
The Controller
Ok, now for the final act, we need to use this new abstraction in our controller method.
Chapter 3 the Servi Ce Container

60
Creating the Controller
Let’s first start by creating a controller we will specifically use for parsing RSS feeds called
the FeedController .
The craft command will postfix controller with Controller , so we just need to run
this:
craft controller Feed
Setting Up the Controller
Now here is the part that is very simple, but it could get a bit tricky the first time you did
it. Instead of importing and using the concrete class we created earlier, we will import
the abstract class we created first.
This means that instead of importing and using FeedRSSParser , we will be importing
and using the RSSParser abstract class we created instead.
So let us import this class and return it in our controllers show method now. We’ll
use an iTunes podcast RSS feed for now at https://rss.itunes.apple.com/api/v1/
us/podcasts/top-podcasts/all/10/explicit.rss . Here is an example of the full
controller:
from app.helpers.RSSParser import RSSParser
class FeedController:
"""FeedController Controller Class."""
def __init__ (self, request: Request):
self.request = request
def show(self, parser: RSSParser):
return
parser.parse('https://rss.itunes.apple.com/api/v1/us/podcasts/top-podcasts/
all/10/explicit.rss')
Chapter 3 the Servi Ce Container

61
Refresher on Abstractions and Concretions
Remember about abstractions vs. concretions and how the container can match them
up. This is an important concept to grasp. You do not need to do it this way, but coding
to an abstract class instead of a concrete class is typically a good design pattern to
follow . It will make code much more maintainable for 2 or 3 years down the road when
one library is abandoned or you need to change to a better or faster library.
In the cases of switching the implementation, you only have to switch out the
binding in the service provider and you are done .
The Route
The last step is to set up the route so we can hit this controller method. This is a very
simple step that we have gone over already.
Get().route('/feed', 'FeedController@show').name('feeds'),
Great! Now when we go to the /feed route, we will see the iTunes podcast feed as you
can see in Figure  3-1 .
Figure 3-1. The RSS feed response
Chapter 3 the Servi Ce Container

63 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_4
CHAPTER 4
Accepting Data
with Forms
In previous chapters, we learned a bit about some of the patterns Masonite uses to
organize an application. We learned about binding and resolving from the container.
We also saw how managers, drivers, and factories are used to create a highly customizable
system.
In this chapter, we’re going to dive back into the practical aspects of building an
application, using these new techniques and tools.
“How Do I Store Data?”
There are many ways to send data, to a web application, using a browser. There are the
obvious ways, such as when we enter a web site address into the browser’s address bar.
We’re telling the browser where we want to go, and that request ends up at the web
application’s doorstep.
In Chapter 2, we saw the many ways in which we can make requests to a Masonite
application. What I’m more interested in, for the purposes of this chapter, are some of
the other ways we can send and receive data.
Have you heard the term “Ajax” before? It’s a name that began as an acronym for a
particular set of technologies ( Asynchronous JavaScript And XML), but has become a
term to describe many kinds of partial page loading.
In essence, Ajax is when the GET or POST requests we usually send happen quietly
behind the scenes, usually to persist some state or reload part of the page with new content.
Then there’s web sockets. These are an evolution of the HTTP requests we’ve seen
thus far. Instead of full or partial requests for new content, web sockets are a continuous
open connection, through which the server can push new content to the browser.

64
There are more ways, but these help to illustrate a problem I want us to solve.
When we’re building web applications, we need to be able to send data along these
channels. We also need to validate that the data is in order, before doing something with
it. Typically, form data is stored, but it could also be sent to other services, which will
expect certain things in certain formats.
So, in this chapter, we’re going to work out how to create forms and how to post their
data securely to the server. We’ll explore the options we have for ensuring the data is
properly formatted and doesn’t try to do malicious things on the server.
Building Secure Forms
This code can be found at https://github.com/assertchris/friday-
server/tree/chapter-5 .
Let’s pick up where we left off, at the end of Chapter 2. We’d built a couple pages,
including one to list search results for podcasts, as shown in Figure  4-1 .
Figure 4-1. W hat we have, so far
Chap Ter 4 aCC epTing Da Ta wi Th Forms

65
We’ll begin by making this page dynamic. The first time someone gets to it, we can
show an empty search result. We do this by sending an empty list of podcasts to the
template and using what’s called a conditional:
from masonite.controllers import Controller
from masonite.view import View
class PodcastController(Controller):
def show_search(self, view: View):
return view.render('podcasts.search', {
'podcasts': self.get_podcasts()
})
def get_podcasts(self, query=“):
return []
This is from app/http/controllers/PodcastController.py .
@extends 'layout.html'
@block content

Podcast search



{{ csrf_field }}





@if podcasts|length > 0
@for podcast in podcasts
@include 'podcasts/_podcast.html'
@endfor
@else
No podcasts matching the search terms
Chap Ter 4 aCC epTing Da Ta wi Th Forms

66
@endif

@endblock
This is from resources/templates/podcasts/search.html .
Since we’re making the list of podcasts dynamic, we’ve created a PodcastController
method to return that list. It’s returning an empty array, for now, but we’ll expand it over time.
That array is passed to the podcasts/search.html template, by supplying a
dictionary to view.render . Then, in the template, we replace the preview static content
with a bit of dynamic code. We check to see if there are any podcasts, failing which we
render some helpful text.
If there are podcasts, we loop over them. There are loads of things going on here,
so we’re going to spend some time looking at what this template is doing and what
templates can do in general. Buckle up!
Template Conditionals
Masonite templates are a superset of Jinja2 templates. That means, anything you can do
in an ordinary Jinja2 template you can do in Masonite. Masonite includes some extra
goodies, like the alternate block syntax.
Here are some ways you can interact with data from the controller:
1. If statements
These are the simplest checks we can do, inside a template. They
take a variable or expression which doesn’t need to be a Boolean.
The value of the variable or expression is interpreted as either
True or False . If True , the nested block will be displayed.
When we say @if podcasts|length > 0 , we’re saying “if the
number of podcasts is greater than zero, show the next nested
level of content.” We can also define an @else block and multiple
@elif blocks.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

67
I personally don’t like the idea of using @elif blocks, as they tend
to make templates messy very quickly. It’s far clearer to define
multiple templates and to do as much conditional logic as is
practical inside the controller.
2. Loop statements
These help us to render a block of content/markup for each item
in a list. In our example application, we may use them to render a
list of podcasts, as we’re doing in the preceding example.
Notice the difference between @endfor and @endif . These help
the compiler to know which kind of conditional block is being
closed, so it’s important to use the appropriate closing block.
It’s something that takes getting used to, especially since Python
doesn’t have block terminators like this.
3. Include statements
These are useful for including other templates into the current
one. We could, for instance, put the block we render for each
podcast into another template and include it inside the loop.
The included template has access to all the variables defined
in the template which includes it. We don’t need to “pass them
down” or anything. We can just start using them straight away.
4. Extend/block statements
These are great for extending an existing layout, as we learned
about in Chapter 3. We’re going to learn more about blocks, as we
add more JavaScript to our application.
{ou can see more details in the official documentation: Views - masonite
Documenta tion.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

68
Template Filters
In addition to what you can do, using Masonite block syntax, there are a bunch of filters
which Jinja2 ships with:
1. value|‘default’
When we get around to showing podcast details, we’ll see
this filter used more. It says, “if the value is not false, show it.
Otherwise, show the value 'default' .” It’s great for filling the gaps
where there is no content to show.
2. items|first
This filter shows the first item from a list of items. It’s useful if you
have a list of things, but you only want to show the first. Of course,
you could always pull the first item out the list, in the controller,
and only send that to the view.
3. ‘hello %s’|format(name)
This filter works like the Python string interpolation method. It’s
useful if you want to use a template string inside the template,
and you have access to variables you want to replace placeholders
with.
4. items|join(‘ , ’)
This filter helps to combine a list of items into a single string, using
another string to go between each item. If the list is only one item
long, the “join” string won’t be added at all.
5. items|last
Similar to first but it returns the last item.
6. items|length
This filter returns the length of a list of items. It’s essential for
pagination and summarizing list contents in search results.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

69
7. it ems|map(attribute=‘value’) or items|map(‘lower’)|join(‘ , ’)
map is an extremely powerful filter. With it, we can pluck attributes
out of each object in a list or provide another filter to be applied
for each item in a list. Then it can even be combined, by extracting
an attribute and then applying another filter to each extracted
value.
8. items|random
Returns a random item from a longer list of items.
9. valu e|reverse
Reverses n object (like a string), or returns an iterator that
traverses the items in a list in reverse.
10. items|sort or items|sort(attribute=‘name’,reverse=True)
This filter sorts a list of items. If the items are strings, just using
|sort should be enough, though you might also want to change
the reverse parameter to make it sort descending. If the items are
dictionaries, you can select which attribute to sort by.
11. valu e|trim
Trims the whitespace before and after a string.
There are quite a few filters not covered in this list. i think some of them are simple
but not as useful, while others are a bit more in-depth that i’d like us to go at
this point. if you’re searching for a filter you don’t see here, check out the Jinja2
filter documentation: https://jinja.palletsprojects.com/en/2.10.x/
templates/#list-of-builtin-filters .
CSRF Protection
One thing I want to mention, before we look at how to use this form on the back end, is
the {{ csrf_field }} field. CSRF (or Cross- Site Request Forgery) is a security concern
that arises when you start to use forms on your site.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

70
Web applications, which require users to log in to perform sensitive operations,
might store some of those credentials in the browser. That way, when you navigate from
page to page (or when you return to the site after a while), you are still logged in.
The problem with this is that malicious folks can forge a request from you to the
web application that requires authentication. Imagine you are logged in to Facebook, in
your browser. While you’re browsing an unrelated site, that site uses an Ajax request to
navigate your browser to the Facebook URL that causes your account to follow theirs.
That can’t happen because Facebook is using a thing called CSRF protection. It adds
a special token to the page from which your account could naturally follow another
account. Then, when your browser initiates the request to follow another account,
Facebook compares the token it has remembered for you with the token the HTTP
request passed along.
If they match, your browser must have proceeded through a natural path to initiate
the follow operation.
i don’t want to dwell too much on the details of this, except to say that masonite
provides a simple mechanism to use the same securit y Facebook uses. {{ csrf_
field }} creates a hidden field, which holds this C sr F token. if your forms don’t
use {{ csrf_field }} , you probably won’t be able to submit their contents to
another masonite U rL, by default.
To a lesser degree, CSRF protection also makes it harder for automation scripts (or
bots) to use your web application. They have to do double the number of requests and
adapt to changes in the markup of the page where they find the initial token.
CSRF can affect web applications that perform destructive or sensitive operations
through HTTP GET requests. It’s just that good applications seldom perform these kinds
of operations through GET requests, because that goes against the original design of the
HTTP specification. You should do the same.
In Chapter 2, we glimpsed CSRF, while we were adding exceptions to some middleware.
It’s important to remember that, while we shouldn’t make a habit of it, we definitely can
bypass this built-in CSRF protection. If there are HTTP endpoints we want to “open up” to
other services, we can do so by adding them to the CSRF middleware exceptions list :
"""CSRF Middleware."""
from masonite.middleware import CsrfMiddleware as Middleware
Chap Ter 4 aCC epTing Da Ta wi Th Forms

71
class CsrfMiddleware(Middleware):
"""Verify CSRF Token Middleware."""
exempt = [
'/home',
'/home/@name',
'/paypal/notify',
]
very_request = False
token_length = 30
This is from app/http/middleware/CsrfMiddleware.py .
A really good example of this is that services like PayPal and Stripe will, at our
request, send us details about payments made by our customers. We’re not going to be
using them, for our home automation, but you’re likely to encounter something similar
the more you build.
Services like these need a way to send us HTTP POST requests, without jumping
through the CSRF hoop. They’re not first going to open a form in a browser and find the
CSRF token.
The trick is being specific about which endpoints are allowed to bypass the built-in
protection and making sure they are bulletproof.
What happens when people call these endpoints with a valid user session in the
browser? What about when they call the endpoints with malicious data? What about
when the endpoint is hammered by bots?
These are the questions you should ask, before allowing an endpoint to bypass the
protection.
Validating Form Data
Once the form is submitted, we need to check that the data it provides is valid. You could
do this in the same controller action you used to display the search page, but I suggest
you split these actions up a bit.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

72
it’s much easier to figure out where a change needs to happen when you’re not
combining multiple hTT p request methods and paths into the same action.
from masonite.request import Request
from masonite.validation import Validator
# ...snip
def get_podcasts(self, query=“):
if query:
dd(query)
return []
def do_search(self, view: View, request: Request,
validate: Validator):
errors = request.validate(
validate.required('terms')
)
if errors:
request.session.flash('errors', errors)
return request.back()
return view.render('podcast.search', {
'podcasts': self.get_podcasts(request.input('terms'))
})
This is from app/http/controllers/PodcastController.py .
Masonite ships with a powerful validation class, one that we’ll undoubtedly reuse
through this book. This is the simplest way to use it :
1. We type hint the Request and Validator parameters to our search
action. Masonite’s container, which we learned about in Chapter 3,
reflects over the parameters to see which objects it should inject
into the function call.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

73
2. W e use the validate method, of the Request class, with a list of
validations we want to perform. The Validator class provides
different rule-generating methods we can use to define what valid
data looks like.
3. If ther e are errors, we find a reasonable way to notify the user
of these errors. Flashing them to the session, which we learned
about in Chapter 2, allows us to remember them temporarily.
Then, after the redirect, we can display them for the user.
@if session().has('errors')

@for field in session().get('errors')

{{ session().get('errors')[field]|join('. ') }}

@endfor

@endif
This is from resources/templates/podcasts/search.html .
If there are validation errors, we want to be able to show them in the search template.
Here, we have access to a session() function, which is a shortcut to the same request.
session object we see in the controller.
If the session has an errors value, we show an enumeration of the fields it contains
errors for. In a simple array, @for item in items will return values we can put directly
into markup. For dictionaries, it becomes @for key in items . Each key is the name of a
field with failed validation.
We then dereference those errors (where each field name, or key, has an array of
error messages) and join them with the join filter we just learned about.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

74
There are many built-in validation methods. so many, in fact, that i’d prefer we
uncover them as we progress through the book instea d of all at once. if you can’t
wait, head over to the official documentation to learn more:
https://docs.masoniteproject.com/advanced/validation .
Fetching Remote Data
Now that we’re getting and validating search terms, it’s time to fetch a list of matching
podcasts. We’ll tap into iTunes to find new podcasts and parse their data.
To begin with, we’ll need a library to make remote requests:
pip install requests
In Chapter 3, we learned about creating service providers. Let’s recap what we
learned.
First, we created a new class, using a craft command:
craft provider RssParserProvider
Figure 4-2. Rendering error messages in a template
Chap Ter 4 aCC epTing Da Ta wi Th Forms

75
We registered this, in config:
# ...snip
from app.providers.RssParserProvider import RssParserProvider
PROVIDERS = [
# ...snip
RssParserProvider,
]
This is from config/providers.py .
This new provider bound a parser class to the IoC container:
from masonite.provider import ServiceProvider
from app.helpers.RssParser import RssParser
class RssParserProvider(ServiceProvider):
wsgi = False
def register(self):
self.app.bind('RssParser', RssParser())
def boot(self):
pass
This is from app/providers/RssParserProvider.py .
And, this RssParser class used a third-party library, called feedparser
(https://pythonhosted.org/feedparser/index.html ), to parse a feed URL :
import feedparser
class RssParser:
def parse(url):
return feedparser.parse(url)
This is from app/helpers/RssParser.py .
Chap Ter 4 aCC epTing Da Ta wi Th Forms

76
We’re going to repeat the process when we bind an HTTP requests library to the IoC
container. We’ll use a library called Requests ( https://2.python-requests.org/en/master ),
beginning with the new provider:
craft provider HttpClientProvider
Then, we need to bind an HttpClient inside that provider:
from masonite.provider import ServiceProvider
from app.helpers.HttpClient import HttpClient
class HttpClientProvider(ServiceProvider):
wsgi = False
def register(self):
self.app.bind('HttpClient', HttpClient())
def boot(self):
pass
This is from app/providers/HttpClientProvider.py .
We also need to add this provider to config:
# ...snip
from app.providers import (
HttpClientProvider,
RssParserProvider
)
PROVIDERS = [
# ...snip
HttpClientProvider,
RssParserProvider,
]
This is from config/providers.py .
This kind of import shorthand is only possible if we also create an init__ file:
from .HttpClientProvider import HttpClientProvider
from .RssParserProvider import RssParserProvider
Chap Ter 4 aCC epTing Da Ta wi Th Forms

77
This is from app/providers/init.py .
The HttpClient class is just a proxy to the requests library:
import requests
class HttpClient:
def get( *args):
return requests.get( *args)
This is from app/helpers/HttpClient.py .
Resolving Dependencies from the Container
Now that we have these tools at our disposal, we need to get them out of the container, so
we can use them to search for new podcasts:
from masonite.controllers import Controller
from masonite.request import Request
from masonite.validation import Validator
from masonite.view import View
class PodcastController(Controller):
def __init__ (self, request: Request):
self.client = request.app().make('HttpClient')
self.parser = request.app().make('RssParser')
def show_search(self, view: View):
return view.render('podcasts.search', {
'podcasts': self.get_podcasts()
})
def get_podcasts(self, query=“):
if query:
dd([query, self.client, self.parser])
return []
Chap Ter 4 aCC epTing Da Ta wi Th Forms

78
def do_search(self, view: View, request: Request,
validate: Validator):
errors = request.validate(
validate.required('terms')
)
if errors:
request.session.flash('errors', errors)
return request.back()
return view.render('podcasts.search', {
'podcasts': self.get_podcasts(
request.input('terms')
)
})
This is from app/http/controllers/PodcastController.py .
We’ve added an init__ method, which resolves HttpClient and RssParser out of
the container.
This isn’t the only way to resolve these dependencies, and the alternatives
definitely deserve some consideration. we’ll circle back to them before too long.
Now, all that remains is to make the iTunes request and parse the results of a search:
def get_podcasts(self, query="):
if query:
response = self.client.get(
'https://itunes.apple.com/search?media=podcast&term=' +
query)
return response.json()['results']
return []
This is from app/http/controllers/PodcastController.py .
Chap Ter 4 aCC epTing Da Ta wi Th Forms

79
iTunes provides a neat, open HTTP endpoint, through which we can search for new
podcasts. The only thing that remains is for us to format the data we get back from this
endpoint :

style="background-image: url('{{podcast.artworkUrl600}}');
background-size: 100% auto; background-repeat: no-repeat;
background-position: center center; ">




{{ podcast.collectionName }}







{{ podcast.artistName }}







Chap Ter 4 aCC epTing Da Ta wi Th Forms

80
Figure 4-3. Finding new podcasts, in an app we built!
This is from resources/templates/podcasts/_podcast.html .
I’ve commented out some of the fields, because we’d need to parse each podcast’s
RSS feed to find that information. That’s definitely possible now that we can pull the RSS
feed parser from the IoC container, but I feel like we’ve achieved enough for this chapter,
already.
Summary
We covered a lot of things in this chapter. There’s also a lot of things we could add.
Consider it a challenge to find out more information, about each podcast, and fill out the
_podcast.html template a bit more.
Aside from learning all about forms and templates, we also got a chance to further
cement what we learned about the IoC container and how to add our own services to it.
In the next chapter, we’re going to explore how to persist this kind of data to a
database, and all that entails.
Chap Ter 4 aCC epTing Da Ta wi Th Forms

81 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_5
CHAPTER 5
Using a Database
In the previous chapter, we learned all about forms. We created a few and even made
them fetch data from remote sources. That’s useful, but not as much unless we can also
store and retrieve data from our own data sources. So, in this chapter, we’re going to
learn about how to set up a database, how to store data in it, and how to pull that same
data back out of it.
How Do I Store Data?
I’ve already hinted at one way, but in truth there are many other ways to store and
retrieve data. We could go “old school” and use flat files of XML or JSON data. It’s
certainly one of the simplest ways, but it suffers from problems like file locks and limited
distribution.
We could use something like Firebase, which is still a database, but it’s not one we
have to manage and control. It also costs more than just using a database on the same
server. It’s a bit harder to administer, and it’s not as fast as it could be.
Instead, we’ll use a local MySQL database (and some SQL to boot) in order to store
our data. Masonite has great support for MySQL databases and even some tools to help
us structure our database. This is going to be fun!
Keeping the Database in Code
This code can be found at https://github.com/assertchris/friday-
server/tree/chapter-6 .

82
Usually, at this point in a book, the author might ask you to step out to another
application. They might ask you to start planning and building your database directly
and completely disconnect from your code editor. I’m not going to ask you to do that for
a couple reasons:
1. I believe database can and should be represented in the code of
your application, because that’s where they are tested, and that’s
the number one place you need to understand them.
2. Masonite provides tools to do it. All the frameworks I like to use
provide these tools. It’s a solved problem!
Let’s say we wanted to start storing podcasts (as the result of “subscribing” to them,
through our existing UI). We might decide to store those podcast URLs, together, in the
users table. Perhaps in a text field, and delimited by commas.
Alternatively, we might want to create a new table and call it subscriptions. This
second approach feels a lot cleaner, to me, as some users may not even want to subscribe
to podcasts in the first place. They might want to listen to music, instead!
To get started, we need to create a thing called a migration, using craft :
craft migration create_subscriptions_table
This will create a new, and empty, migration:
from orator.migrations import Migration
class CreateSubscriptionsTable(Migration):
def up(self):
"""Run the migrations."""
pass
def down(self):
"""Revert the migrations."""
pass
This is from database/migrations/x_create_subscriptions_table.py .
Chap Ter 5 Using a Da Tabase

83
There are two parts to a migration:
1. up  – Where new additions/changes are made to the existing
database structure
2. down  – Where these new additions/changes can be rolled back, in
case there’s a problem or the migration happened too soon
Let’s define a new table:
from orator.migrations import Migration
class CreateSubscriptionsTable(Migration):
def up(self):
with self.schema.create('subscriptions') as table:
table.increments('id')
table.string('url')
table.string('title')
table.timestamps()
def down(self):
self.schema.drop('subscriptions')
This is from database/migrations/x_create_subscriptions_table.py .
To begin with, our subscriptions table is kinda small and simple. We’re going to store
the title of a podcast and the URL where the podcast details may be found. We create a
table by calling the schema.create method. This returns a new table object, which we
can call various methods on, to create fields for in the table.
There are a few fields which are quite common and important :
1. increments  – An auto-number integer field, which is the primary
key for the table
2. timestamps  – A couple timestamp fields, to remember when
certain events took place (like when the record was created to last
updated)
Chap Ter 5 Using a Da Tabase

84
There are many other field types, too:
1. string  – A length-limited string field
2. text  – A variable-length string field
3. integer  – An integer field
4. float  – A decimal field
5. timestamp  – A timestamp field
Fields may also have modifiers on them, which affect the metadata of the field. We
could apply one of these, for instance:
1. nullable  – When the field is allowed to contain NULL as a value
2. default(value)  – For the default value a non-nullable field
should have
3. unsigned  – For any of the numeric fields, so they can store twice as
many nonnegative numbers
There are quite a few field types i’ve not mentioned here. [ou can refer to the
Orator documentation, if you’re looking for something that’s missing. Orator is the
name of the underlying database library, which makes all of this possible.
Creating new tables is one reason to make a migration, but you might also want to
change the structure of a table. In that case, you’d use the schema.table method:
from orator.migrations import Migration
class ChangeSubscriptionsTable(Migration):
def up(self):
with self.schema.table('subscriptions') as table:
table.string('title', 200).change()
def down(self):
with self.schema.table('subscriptions') as table:
table.string('title').change()
This is from database/migrations/x_change_subscriptions_table.py .
Chap Ter 5 Using a Da Tabase

85
Aside from changing a field, this is also a good example of how to use the down
method. The idea is that anything you add to or change in the database is “reverted” in
the down method. We changed the title field to be length limited, so the rollback of this
would be to remove that 200-character limit.
Similarly, we could also call a table.dropColumn(name) method to remove a field or
a schema.drop(name) method to drop the table entirely.
it takes a bit of time to come around to this way of thinking about database tables.
i encourage you to take a read through the Orator documentation, for managing
migrations, so you can get familiar with all the different things you can do in a
migration.
Before we can run these migrations, we probably need to make sure everything
is set up. You should have a MySQL database installed. If you’re on macOS (and have
Homebrew installed), you can do this:
brew install mysql
For other systems and configurations, check out the Orator configuration
documentation.
You’ll also need to install one of the database dependencies:
pip install mysqlclient
Finally, you’ll need to make sure your .env database credentials match up to a
database you’ve already created:
DB_DATABASE=Friday
DB_USERNAME=
DB_PASSWORD=
homebrew uses username “root” and password “” by default. These are not
what i’d call secure credentials, but it’s good to know about them if this is the first
time you’re using My sQL on your system. [ou can, of course, change them to suit
your needs. even with these credentials, you’ll still need to make sure My sQL is
running and that you’ve created a database to match the one you’ve configured.
Chap Ter 5 Using a Da Tabase

86
Filling the Database with Dummy Data
Some folks test their applications with an empty database, or by manually inserting data
by using the site. This can be a bit of a trap, because it means the data that they insert
conforms to how they expect the site to be used, and seldom covers all the important
states a particular part of the app can be in. Let’s think about some of the different states
our application could be in:
• The empty search screen, before we search for a podcast
• The empty search screen, when no results are found
• A “ details” screen, showing the particulars of a podcast
• A “ subscriptions” screen, showing all the podcasts someone is
subscribed to
• An em pty “subscriptions” screen, when the user hasn’t subscribed to
any podcasts
Not to mention all the confirmation screens, for subscribing and unsubscribing to
podcasts.
And, this is just one data type in what might become a huge application! Imagine
trying to test all these things manually. You’d probably forget about half the pages, and
the manual testing would take ages (or just not happen).
Beyond these problems, imagine the kinds of data you’d have in the app:
• Would you cater for podcasts with huge titles?
• Would you cater for search results numbering in the hundreds?
• Could your application handle Unicode characters in podcast titles?
Filling the database with test data (or seeding, as it’s commonly referred to) is an
important design step, because it helps you remember all the edge cases and states you
need to think about. When combined with testing (which we’ll get to in Chapter 15 ),
seed data forces a design to be robust.
The question becomes: How do we seed database data? There’s a craft command
for that:
craft seed subscriptions
Chap Ter 5 Using a Da Tabase

87
This creates a new seed(er) file, which looks like this:
from orator.seeds import Seeder
class SubscriptionsTableSeeder(Seeder):
def run(self):
pass
This is from database/seeds/subscriptions_table_seeder.py .
We can change this, slightly, so that we’re sure it’s running:
from orator.seeds import Seeder
class SubscriptionsTableSeeder(Seeder):
def run(self):
print('in the seeder')
This is from database/seeds/subscriptions_table_seeder.py .
Before this will run, we need to add it to the “base” seeder:
from orator.seeds import Seeder
# from .user_table_seeder import UserTableSeeder
from .subscriptions_table_seeder import SubscriptionsTableSeeder
class DatabaseSeeder(Seeder):
def run(self):
# self.call(UserTableSeeder)
self.call(SubscriptionsTableSeeder)
This is from database/seeds/database_seeder.py .
This seeder is the entry point through which craft will run all the other seeders. I
have commented out the user stuff, because we don’t need it until Chapter 8. I have also
added the subscriptions seeder and called it using the self.call method.
Chap Ter 5 Using a Da Tabase

88
Let’s seed the database, to see if the subscriptions seeder is running:
craft seed:run
> in the seeder
> Seeded: SubscriptionsTableSeeder
> Database seeded!
If you also see the “in the seeder” text, then the subscriptions seeder is working. Let’s
learn a bit about how to read from and write to the database.
Writing to the Database
It would be helpful to have a database UI application running, so you can see the things
we’re about to do to the database. I highly recommend TablePlus or Navicat. If you’re
looking for something cheaper, check out HeidiSQL.
Ye’re about to learn how to interact with a database, and Orator will generate and
use sQL to do this. [ou don’t need to know sQL, but it will undoubtedly help. Check
out the books apress has on the subject a t www.apress.com/us/databases/
mysql .
Let’s begin writing to the database by faking a subscription. Our subscriptions table
has a couple material fields we need to fill in:
from config.database import DB
from orator.seeds import Seeder
class SubscriptionsTableSeeder(Seeder):
def run(self):
DB.table('subscriptions').insert({
'url': 'http://thepodcast.com',
'title': 'The podcast you need to listen to',
})
This is from database/seeds/subscriptions_table_seeder.py .
Chap Ter 5 Using a Da Tabase

89
The database connection is defined in the config section of our application, and we
can pull the connection instance from there, to write to it. If you’ve got your database
GUI open, you should now see a subscription in the subscriptions table. You should also
see the corresponding SQL statement in the console.
It’s useful that we don’t need to write the full SQL statement out in order for it to
be executed. This is a side effect of Orator trying to build SQL statements that work in
any of the engines it supports. The idea is that we should be able to move to a different
(supported) engine, and all our abstracted SQL statements should continue to work.
There are other kinds of operations we can do, but we’ll get to examples of those
in a bit.
This code is only the first step. If we want our seeders to be really helpful (and our
designs to be robust), we need to use randomized data in the seeding phase. Orator
installs a package, automatically, called Faker. It’s a random fake data generator, which
we can use in our seeder:
from config.database import DB
from faker import Faker
from orator.seeds import Seeder
class SubscriptionsTableSeeder(Seeder):
def run(self):
fake = Faker()
DB.table('subscriptions').insert({
'url': fake.uri(),
'title': fake.sentence(),
})
This is from database/seeds/subscriptions_table_seeder.py .
Chap Ter 5 Using a Da Tabase

90
Now, we can be prepared for different kinds and amounts of data in our designs,
because we don’t control exactly what data goes into them. We’re not only populating
them in ways that we expect the data to look. There are quite a few useful data
types Faker provides, so I’m not going to go into them all. Sufficed to say, the Faker
documentation is amazing, and you should definitely check it out : https://faker.
readthedocs.io/en/stable/providers.html .
Reading from the Database
Inserting data is cool, but how do we get the data back out of the database, so that we
can display it in the parts of the application that need it? Let’s make a page to list the
subscriptions we have.
from config.database import DB
# ...snip
class PodcastController(Controller):
# ...snip
def show_subscriptions(self, view: View):
subscriptions = DB.table('subscriptions').get()
return view.render('podcasts.subscriptions', {
'subscriptions': subscriptions,
})
This is from app/http/controllers/PodcastController.py .
@extends 'layout.html'
@block content

Subscriptions



@if subscriptions|length > 0
@for subscription in subscriptions
@include 'podcasts/_subscription.html'
@endfor
Chap Ter 5 Using a Da Tabase

91
@else
No subscriptions
@endif

@endblock
This is from resources/templates/podcasts/subscriptions.html .

{{ subscription.title }}

{{ subscription.url }}


This is from resources/templates/podcasts/_subscription.html .
RouteGroup(
[
# ...snip
Get('/subscriptions',
'PodcastController@show_subscriptions').name('-show-
subscriptions')
],
prefix='/podcasts',
name='podcasts',
),
This is from routes/web.py .
These four files should be more familiar to you now. The first is an additional
controller action, which responds to the route we create in the fourth. The second
and third files are the markup (views) to show the list of subscriptions. It should look
something like Figure  5-1 in the browser.
Chap Ter 5 Using a Da Tabase

92
Hidden in that new controller action is the database code that pulls the subscriptions
out of the database: DB.table('subscriptions').get() .
Filtering Database Data
What about if we want to filter that list? First we need to add fields to filter by. The most
useful would be to add the ability to “favorite” a subscription, so that it appears at the top
of the list. To this end, we need to create another migration:
from orator.migrations import Migration
class AddFavoriteToSubscriptionsTable(Migration):
def up(self):
with self.schema.table('subscriptions') as table:
table.boolean('favorite').index()
def down(self):
with self.schema.table('subscriptions') as table:
table.drop_column('favorite')
Figure 5-1. Listing subscriptions stored in the database
Chap Ter 5 Using a Da Tabase

93
This is from database/migrations/x_add_favorite_to_subscriptions_
table.py .
In this new migration, we’re adding a boolean field, called favorite , and making an
index for it. The notes migration is reversed; we’re also dropping this column, so that it’s
like it was never there. It may be useful to know that you can roll back all the migrations
and run them all again, using craft :
craft migrate:refresh --seed
We may also need to update the seeder to account for this new field – since we aren’t
allowing the field to be nullable and we’re also not specifying a default value:
from config.database import DB
from faker import Faker
from orator.seeds import Seeder
class SubscriptionsTableSeeder(Seeder):
def run(self):
fake = Faker()
DB.table('subscriptions').insert({
'url': fake.uri(),
'title': fake.sentence(),
'favorite': fake.boolean(),
})
This is from database/seeds/subscriptions_table_seeder.py .
Now that we have a new filterable field, we can split the subscriptions into a list of
“ordinary subscriptions” and “favorite subscriptions”:
@extends 'layout.html'
@block content

Favorites



Chap Ter 5 Using a Da Tabase

94
@if favorites|length > 0
@for subscription in favorites
@include 'podcasts/_subscription.html'
@endfor
@else
No subscriptions
@endif

Subscriptions



@if subscriptions|length > 0
@for subscription in subscriptions
@include 'podcasts/_subscription.html'
@endfor
@else
No subscriptions
@endif

@endblock
This is from resources/templates/podcasts/subscriptions.html .
We can duplicate the block of subscription-based code (and, maybe later, we can
include the other one) so that we can use a different source of subscription items. We
can call it favorites, but that also means we need to provide that from the controller:
def show_subscriptions(self, view: View):
favorites = DB.table('subscriptions').where('favorite', True).get()
subscriptions = DB.table('subscriptions').where(
'favorite', '!=', True).get()
return view.render('podcasts.subscriptions', {
'favorites': favorites,
'subscriptions': subscriptions,
})
Chap Ter 5 Using a Da Tabase

95
This is from app/http/controllers/PodcastController.py .
Here, we’re using the where method to filter subscriptions by whether or not their
favorite field has a truth value. It’s one of the many useful query methods, including
• where with two arguments, where the first is the field and the second
is the value
• where with three arguments, where the middle argument is the
comparison operator (like how we’re using != to say “not equal to”)
• where_exists with a single query object, so that the outer query only
returns results when the inner query does (similar to a left join)
• where_raw with a raw where clause string (like subscriptions.favorite = 1 )
There are some subtitles to these, which you can find by reading the documentation
at https://orator-orm.com/docs/0.9/query_builder.html#advanced-where . It’s not
really important to remember exact syntax, but rather to be aware that these methods
exist so that you know where in the documentation to go to learn more about them.
if we were to make the favorite field nullable, then the second query would
ca tch all records where favorite wasn’t set to True , including records where
favorite was False and Null . Ye could be a bit more explicit, by saying
where('favorite', False) , but we'd have to remember to change that if we
ever made the favorite field nullable.
Updating Database Data
Let’s add the ability to favorite (and unfavorite) a database record. We’ll need a couple
new controller actions and routes:
def do_favorite(self, request: Request):
DB.table('subscriptions').where('id', request.param('id')).update({
'favorite': True,
})
return request.redirect_to('podcasts-show-subscriptions')
Chap Ter 5 Using a Da Tabase

96
def do_unfavorite(self, request: Request):
DB.table('subscriptions').where('id', request.param('id')).update({
'favorite': False,
})
return request.redirect_to('podcasts-show-subscriptions')
This is from app/http/controllers/PodcastController.py .
In addition to an insert method, we can also use an update method to affect
database records. These two actions are quite similar, but I think it best not to abstract
them into a single method, because this is undeniably clear as to which action does what.
After updating the subscription, we're also redirecting back to the subscriptions
page. We need to set up routes and change the subscription include:
from masonite.routes import Get, Patch, Post, Match, RouteGroup
ROUTES = [
# ...snip
RouteGroup(
[
# ...snip
Patch('/subscriptions/@id/favorite', 'PodcastController@do_
favorite').name('-favorite- subscription'),
Patch('/subscriptions/@id/unfavorite', 'PodcastController@do_
unfavorite').name('-unfavorite- subscription'),
],
prefix='/podcasts',
name='podcasts',
),
]
This is from routes/web.py .
Chap Ter 5 Using a Da Tabase

97

{{ subscription.title }}

{{ subscription.url }}


< form class="inline-flex" action="{{ route('podcasts-favorite-
subscription', {'id': subscription.id}) }}" method="POST">
{{ csrf_field }}
{{ request_method('PATCH') }}
< button onclick="event.preventDefault(); this.form.
submit()">favorite

< form class="inline-flex" action="{{ route('podcasts-unfavorite-
subscription', {'id': subscription.id}) }}" method="POST">
{{ csrf_field }}
{{ request_method('PATCH') }}
< button onclick="event.preventDefault(); this.form.
submit()">unfavorite



This is from resources/templates/podcasts/_subscription.html .
Since we’re using non-GET and non-POST request methods (for the routes), we
need to use forms to initiate the favorite/unfavorite actions. We tell Masonite that these
are PATCH requests using the request_method view helper. We should be able to use the
buttons to toggle a subscription between the lists we’ve created.
Deleting Database Data
The last bit of functionality I want us to add is the ability to unsubscribe from a podcast.
This requires little more code than we’ve already made and learned about :
Chap Ter 5 Using a Da Tabase

98
subscription.id}) }}" method="POST">
{{ csrf_field }}
{{ request_method('DELETE') }}
< button onclick="event.preventDefault(); this.form.
submit()">unsubscribe

This is from resources/templates/podcasts/_subscription.html .
This resembles our PATCH routes, but the appropriate method we need (for an
“unsubscribe”) is DELETE. Similarly, we need to use the Delete route method, when
defining the route:
from masonite.routes import Delete, Get, Patch, Post, Match, RouteGroup
ROUTES = [
# ...snip
RouteGroup(
[
# ...snip
Delete('/subscriptions/@id/unsubscribe', 'PodcastController
@do_unsubscribe').name('-unsubscribe'),
],
prefix='/podcasts',
name='podcasts',
),
]
This is from routes/web.py .
Chap Ter 5 Using a Da Tabase

99
And, we can use the delete method to remove the record from the subscriptions table:
def do_unsubscribe(self, request: Request):
DB.table('subscriptions').where('id', request.param('id')).delete()
return request.redirect_to('podcasts-show-subscriptions')
This is from app/http/controllers/PodcastController.py .
There is so much depth to this part of Masonite that no single chapter can ever do
it justice. This has been a taste, but the only way you’re going to get to grips with all that
Orator has to offer, here, is to dig deep into the document and to actually use Orator to
do different and complex things.
[ou can find detailed documentation, for these D b statements, at https://
orator-orm.com/docs/0.9/query_builder.html#introduction .
Simplifying Code Through Models
Now that we have a handle on writing abstracted database queries, I want us to look at
how these can be simplified by the judicious use of models. Models are what we call
objects that follow the Active Record database pattern. It’s a bit of a tricky concept, at
first. The basic idea is that we define database tables as classes, using static methods to
refer to table-level actions and instance methods to refer to row-level actions.
We can define a new model, using craft :
craft model Subscription
This produces a new class, which looks like this:
from config.database import Model
class Subscription(Model):
"""Subscription Model."""
pass
Chap Ter 5 Using a Da Tabase

100
This is from app/Subscription.py .
This Subscription class extends the Orator Model class, which means it already has
a lot of magic available to reduce the code we’ve already written. We can simplify our
initial set of retrieval queries, by referring directly to the model:
from app.Subscription import Subscription
# ...later
def show_subscriptions(self, view: View):
# favorites = DB.table('subscriptions').where('favorite', True).get()
favorites = Subscription.where('favorite', True).get()
# subscriptions = DB.table('subscriptions').where(
# 'favorite', '!=', True).get()
subscriptions = Subscription.where(
'favorite', '!=', True).get()
return view.render('podcasts.subscriptions', {
'favorites': favorites,
'subscriptions': subscriptions,
})
This is from app/http/controllers/PodcastController.py .
Similarly, we can simplify the seeding and updating and deleting by also referring
directly to the model:
from app.Subscription import Subscription
# from config.database import DB
from faker import Faker
from orator.seeds import Seeder
Chap Ter 5 Using a Da Tabase

101
class SubscriptionsTableSeeder(Seeder):
def run(self):
fake = Faker()
# DB.table('subscriptions').insert({
# 'url': fake.uri(),
# 'title': fake.sentence(),
# 'favorite': fake.boolean(),
# })
Subscription.create(
url=fake.uri(),
title=fake.sentence(),
favorite=fake.boolean(),
)
# ...or
Subscription.create({
'url': fake.uri(),
'title': fake.sentence(),
'favorite': fake.boolean(),
})
This is from database/seeds/subscriptions_table_seeder.py .
The first time you run this, you’re likely to encounter a MassAssignmentError . That’s
because Masonite protects against unintended bulk updates to records. We can bypass
this by adding a special property to the model:
class Subscription(Model):
__fillable__ = ['title', 'url', 'favorite']
This is from app/Subscription.py .
Chap Ter 5 Using a Da Tabase

102
def do_favorite(self, request: Request):
# DB.table('subscriptions').where('id', request.param('id')).update({
# 'favorite': True,
# })
subscription = Subscription.find(request.param('id'))
subscription.favorite = True
subscription.save()
return request.redirect_to('podcasts-show-subscriptions')
def do_unfavorite(self, request: Request):
# DB.table('subscriptions').where('id', request.param('id')).update({
# 'favorite': False,
# })
subscription = Subscription.find(request.param('id'))
subscription.favorite = False
subscription.save()
return request.redirect_to('podcasts-show-subscriptions')
def do_unsubscribe(self, request: Request):
# DB.table('subscriptions').where('id', request.param('id')).delete()
subscription = Subscription.find(request.param('id'))
subscription.delete()
return request.redirect_to('podcasts-show-subscriptions')
This is from app/http/controllers/PodcastController.py .
I’ve left the previous DB calls here, but commented out, so we can compare them to
the model-based code. In some cases, it’s slightly more code to use the model, but the
results are much clearer. As we progress through the rest of the book, you’re going to see
much more model code and much less low-level query code.
Chap Ter 5 Using a Da Tabase

103
Summary
In this chapter, we had our first look into how to use the database. We went all the way
from defining database structure through to representing tables and rows in the form
of models. It’s been a bit of a whirlwind tour, but it’s also foundational for the rest of
the book.
Take some time to experiment with different database queries and actions, and
see how they can be used in model form. Try creating a “subscribe” action, so that
podcasts returned in search results are persisted to the database. If you can achieve
that, given what you’ve learned in this chapter, then you’re on a rocket ship to mastery
of Masonite!
Chap Ter 5 Using a Da Tabase

105 © Christopher Pitt and Joe Mancuso 2020 C. Pitt and J. Mancuso, The Definitive Guide to Masonite , https://doi.org/10.1007/978-1-4842-5602-2_6
CHAPTER 6
Security
Masonite is developed with application security in mind. When a release is getting ready,
it will be reviewed for any possible security vulnerabilities by a maintainer. Masonite also
makes use of services such as DeepSource which will scan each pull request for possible
security vulnerabilities, code smells, possible bugs, and other code issues.
It would be foolish to think all security vulnerabilities will never make it into the
codebase, though, especially because of the fact that new ways to attack an application
can be discovered or invented. There are other ways to handle situations like that, which
we will talk about later in this chapter.
Another important reminder is that when we talk about Masonite and security,
what we are really talking about is application security. Masonite is the application and
we can only protect the application from vulnerabilities. There are many other types of
vulnerabilities that Masonite cannot control. For example, there could be a vulnerability
on your specific version of operating system that is hosting Masonite which can lead to
an exploitation.
So it’s important to note that just because your application is secure, it does not
mean you are not vulnerable. You will need to learn many of the different avenues of
attack and ensure you are protected.
CSRF Protection
CSRF stands for Cross-Site Request Forgery. In the simplest terms, it helps on two fronts:
• Pr otect against bad actors making requests on behalf of a user.
• Pr evention against people sneaking in malicious code to your site to
look like a button or an alert is coming from your site but it’s really
going to another site.

106
Let’s take the example of a login form. A user enters an email and a password and
hits submit. The submission goes to an endpoint where we check if the username and
password are correct and we login the user. But what prevents someone from simply
sending a POST request to that page? Anybody can now just brute force their way by
hitting the endpoint over and over via Postman or cURL.
The other protection is by preventing malicious code from being saved into a
database and later displayed on your site. If someone can save a JavaScript '
}
"""
def show(self, request: Request):
request.input('bad')
then we would actually get a value like

The HTML entities are now escaped and your browser will simply display this as text
if it made it on to your web pages somewhere rather than execute the script.
You can choose not to clean something by passing in clean=False , but you are at
your own risk if you choose to do this. By reading the rest of this chapter, you should be
an expert on application security with Masonite.
Chapter 6 Se Curity

107
The CSRF Token
We will be talking about a CSRF token a lot in this section, so let’s dedicate a few
paragraphs on talking about what it actually is and where it comes from.
The trick behind what the CSRF token is pretty simple: we give a token to the user
and the user sends the token back. The token is just an unpredictable secret string given
to the client and also known by the server. This allows the client to receive the token and
send the token back. If the token we gave the client is not the same as the token we get
back, then we will reject the request.
How do we know it came from our web site? Because the CSRF token is just a cookie
registered to the user that came from our web site. In the beginning of the user’s session
(like when the user hit our site for the first time), a cookie was created called csrf_token
which simply generated a completely random string which is our constant that we can
check later.
When the user submits a form, they will also submit this CSRF token. We will take
that token and check it against the one we saved in the cookie. If the cookie value and the
value of the token they submitted both match, then we can make a very safe assumption
that the user who had the cookie registered to them and the user who submitted the
form are the same people.
Form Submissions
Masonite protects against CSRF attacks for form submissions. The CSRF flow described
previously is exactly how CSRF protection on form submissions works.

{{ csrf_field }}


That {{ csrf_field }} you see in the form actually translates to

when the page is fully rendered. Let’s explain what this is doing because it’s important.
Chapter 6 Se Curity

108
First, it is creates a hidden input ; this means it won’t actually display on the form, but
it will be there and it will submit this input in addition to the input that gets submitted
from the user.
The second thing it’s doing is actually submitting the CSRF token with the name of
__ token so we can fetch it from the back end to do our verification.
Lastly, it sets the value equal to the CSRF token. The CSRF token is generated once
per user session by default (you can bump this up to every request later in the chapter).
When the user submits the form, it will check against the cookie and the user-
submitted token and verify that they match. If it matches, it will let the request go
through; if it does not, then it will block the request because if the values do not match,
then the user either submitted the form maliciously or they manipulated the token
values maliciously. Either way we block the request.
AJAX Calls
AJAX calls are a little different than form submissions. AJAX calls don’t utilize the same
type of request that forms submit. AJAX calls are typically done via JavaScript, do what’s
called an “asynchronous request,” and expect a response back before redirecting or
doing some other logic within JavaScript.
Because of this, the preceding approach with sending a hidden input no longer
works. But don’t worry because you can still send the token along with your request, but
you would just send it within your Ajax call.
Similar to how you can use the {{ csrf_field }} built-in variable, you can use the
{{ csrf_token }} variable to just get the token. We can then put that token inside a
meta tag and put it into our head section of our HTML :

Page Title


Now in order to get the token to pass along with our call, we can simply get the token
from this meta field:
let token = document.head.querySelector('meta[name="csrf-token"]').content;
Chapter 6 Se Curity

109
The rest is up to what JavaScript framework you are using. If you are using jQuery, it
might look something like this:
let token = document.head.querySelector('meta[name="csrf-token"]').content;
$.ajax({
type: "POST",
data: {
'email': 'user@example.com',
'password': 'secret',
'__token': token
},
success: success,
dataType: dataType
});
Notice we are passing the __ token along very similarly to how we were doing it with a
form request, but now we need to pass it more manually.
Some people make the mistake of trying to use CS rF token between servers
or between applications. remember, CS rF tokens are only for use within the
same application. if you need to make requests between applications or between
servers, you should look into using JW t tokens and api authentication methods or
something similar and not CS rF tokens.
Password Reset
If you are first starting a project, it is advised to run the craft auth command which
will scaffold out several views, routes, and controllers for you which handles logging in,
registering, authentication, and password resets. You don’t need to do this, but it will
prevent you from needing to build all of it out yourself and it’s 100% customizable.
One of the most vulnerable parts of an application is the password reset functionality.
Since this part of your application is handling sensitive data like a password, it is a ripe
target for your malicious actors. Protecting your password resets is vital.
There are several different best approaches for resetting your password, but let’s
explain exactly how Masonite does it and we’ll explain some key points along the way.
Chapter 6 Se Curity

110
The Form
The first part of a password reset process is going to the password reset form. By default
this is under the /password route if you are using the default scaffolding. The form is a
submit input to enter the email and a simple submit button.
When the user enters their email and hits submit, we do a lookup of the email in our
database, and if it exists, we send them an email using the email address we have in our
database with a URL appended with a token. They will get a success notification back
that an email has been sent to them and to follow the instructions there.
The second important piece you might have missed is that we send an email to
the user’s email we get from our database and we do not send an email to the user ’s
email they submitted . In other words, we do a lookup of the user in our database with
the email address that the user submitted, but we send an email to the one in our
database . At first glance, this might seem strange; I mean, they are the same email
address, right? Well not exactly. There are differences between Unicode characters and
non-Unicode characters. To the blind eye, they might seem the same.
Unicode Attack
Let’s talk about the Unicode attack which is one of the most dangerous attacks when it
comes to password resets.
First, let’s explain what a Unicode is. Each character in Unicode receives a number
under the hood. The numbers get stored in the format of something like U+0348 and then
can be decoded on all systems that support Unicode (which is nearly all systems). It’s
basically a character encoding standard. The issue is that some Unicode characters are
remarkably similar to other Unicode characters.
Let’s take these two email addresses:
John@G ıthub.com
John@Github.com
Looks kind of similar, right? If you do a double take, you might realize something
strange with the i in GitHub ; there is no dot above it.
Let’s try to do a comparison now:
>>> ' ı' == 'i'
False
Chapter 6 Se Curity

111
Doing a comparison returns False, but now let’s convert them both to upper:
>>> ' ı'.upper() == 'i'.upper()
True
This is because some Unicode characters have “Case Collisions,” meaning when i
is converted to uppercase and ı is converted to uppercase, they are both I. Now they
match. What’s even scarier is we can take the original email addresses we started with
before and we can do the same comparison:
>>> 'John@G ıthub.com'.upper() == 'John@Github.com'.upper()
True
The email addresses are visually different but actually evaluate True. This is where
the attack comes in.
A user can submit the email address John@G ıthub.com for a password reset ; we could
possibly find a match for John@Github.com in the database and then send an email
address to the incorrect John@G ıthub.com address, thereby having an exploit in our
password reset form.
This is why we make sure to send an email to the address we have in our database
because it makes sure we are sending the reset instructions to the correct email address.
SQL Injection
Since we’re already on the topic of attacks, we should also talk about another very
common attack called SQL injection. This is probably the most common attack, and if
you have been in software development for more than 5 minutes, you have heard of this.
SQL injection is actually really simple. SQL injection happens when you don’t
properly sanitize incoming user data and you then use that data to make queries. Let’s
take a simple code snippet of what might look like with Masonite and Orator:
def show(self, request: Request):
User.where_raw(f"email = {request.input('email')}").first()
On a normal attempt, this could generate a query like this:
SELECT * FROM `users` WHERE `email` = 'user@example.com'
Chapter 6 Se Curity

112
Seems pretty innocent, but that is assuming the request input is equal to user
@example.com . What if it was equal to something more malicious, say, user@example.com;
drop table users; ?
Now that query looks something like
SELECT * FROM `users` WHERE `email` = user@example.com; drop table users;
This is actually now two queries. SQL will try to run the first query (and probably
throw a syntax error) and then will try to run the second query, which is valid, and
actually drop the users table.
The user now “injected” query into our database because we had a code
vulnerability.
Query Binding
Notice in the preceding code example we used a raw query. I demonstrated this example
because a code example might look like this:
def show(self, request: Request):
User.where('email', request.input('email')).first()
In this example, the query is actually a bit different. Orator will now generate a
query like this:
SELECT * FROM `users` WHERE `email` = ?
It will then send as part of a second step what the input is to the database.
Underlying database packages will then be responsible for sanitizing the input to ensure
nothing malicious is going on before sending to the database.
Mass Assignment
Orator has a very special kind of way of interacting with classes and databases. You’ll
notice that Orator models are extremely bare bones because Orator handles all the heavy
lifting for you.
First, let’s talk about the two methods that are actually mass assignment for Orator.
Mass assignment is anything that updates a table from a bulk of inputs.
Chapter 6 Se Curity

113
For example, these two lines of code are mass assignment :
def show(self, request: Request):
User.create(request.all())
User.find(1).update(request.all())
This code snippet is not mass assignment :
def show(self, request: Request):
user = User.find(1)
user.admin = request.input('is_admin')
user.save()
The design pattern of Orator opens up the door to an attack called a mass
assignment attack.
Let’s take a look at this code snippet and then we’ll walk through it :
"""
POST {
'email': 'joe@masoniteproject.com',
'name': 'Joe Mancuso',
'password': 'secret'
}
"""
def show(self, request: Request):
User.create(request.all())
If we have a simple request input, this query might look something like this:
INSERT INTO `users` (`email`, `name`, `password`)
VALUES ('joe@masoniteproject.com', 'Joe Mancuso', 'secret')
This seems pretty innocent, but it leaves the door open for a user passing any
information in. For example, they might pass in if they are an admin and all that they
would have to do would be to pass those values in:
"""
POST {
'email': 'joe@masoniteproject.com',
Chapter 6 Se Curity

114
'name': 'Joe Mancuso',
'password': 'secret',
'admin': 1
}
"""
def show(self, request: Request):
User.create(request.all())
This would generate a query like this:
INSERT INTO `users` (`email`, `name`, `password`, `admin`)
VALUES ('joe@masoniteproject.com', 'Joe Mancuso', 'secret', '1')
Now the user just made themselves an admin pretty simply.
Fillable
In order to protect against this attack, Orator made a __ fillable__ property you can put
on your model. Now we can do something like this:
class User:
___fillable__ = ['email', 'name', 'password']
Now it will ignore any fields that try to get mass assigned. Going back to the
vulnerable code snippet :
"""
POST {
'email': 'joe@masoniteproject.com',
'name': 'Joe Mancuso',
'password': 'secret',
'admin': 1
}
"""
def show(self, request: Request):
User.create(request.all())
Chapter 6 Se Curity

115
It will now correctly generate a query like this:
INSERT INTO `users` (`email`, `name`, `password`)
VALUES ('joe@masoniteproject.com', 'Joe Mancuso', 'secret')
It will ignore everything that is not inside the __ fillable__ property.
CORS
Most people interactions with CORS are from trying to access a server that implements
CORS and then trying to get around CORS because people don’t quite understand it.
CORS stands for Cross-Origin Resource Sharing and what it does is it allows servers
to tell browsers through HTTP headers which specific resources are allowed to be
accessed and how those resources should be accessed. For example, a server might tell
a browser to only send requests to example.com if the request is coming from site.com .
Maybe we have some kind of microservice and we want to make sure only applications
from our application at site.com .
The way browsers handle this is they do something called a preflight request which
is a simple HTTP request they send right before they send the payload. That preflight
request is used to essentially “scout” the server and check if the CORS instructions match
what they are about to send. If they do not match, then the browser will throw an error
related to the CORS instructions not being valid.
This is not a surefire way to protect your application, but it does add a layer of
security and request validation.
CORS Provider
Masonite allows your application to return CORS headers so we can help protect our
application. To do so is pretty simple. We can simply add that provider to the provider
configuration list right below your AppProvider :
# config/providers.py
# ...
from masonite.providers import CorsProvider
Chapter 6 Se Curity

116
PROVIDERS = [
AppProvider
CorsProvider,
# ...
]
Now your server will start returning the following CORS headers and browsers will start
enforcing your rules.
Lastly you can add some sensible defaults to the bottom of your config/middleware.py
file:
# config/middleware.py
# ...
CORS = {
'Access-Control-Allow-Origin': " *",
" Access-Control-Allow-Methods": "DELETE, GET, HEAD, OPTIONS, PATCH,
POST, PUT",
"Access-Control-Allow-Headers": "Content-Type, Accept, X-Requested- With",
"Access-Control-Max-Age": "3600",
"Access-Control-Allow-Credentials": "true"
}
This will now set these headers when people visit your application:
Access-Control-Allow-Origin: *,
Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT,
Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With,
Access-Control-Max-Age: 3600,
Access-Control-Allow-Credentials: true
You can modify the headers by modifying the key and values of the CORS variable in
your middleware file. You’ll need to create this if you don’t see it there:
# config/middleware.py
# ...
CORS = {
"Access-Control-Allow-Origin": " *",
Chapter 6 Se Curity

117
" Access-Control-Allow-Methods": "DELETE, GET, HEAD, OPTIONS, PATCH,
POST, PUT",
"Access-Control-Allow-Headers": "Content-Type, Accept, X- Requested- With",
"Access-Control-Max-Age": "3600",
"Access-Control-Allow-Credentials": "true"
}
Feel free to modify these headers or even look up additional headers you can add.
Secure Headers
Similar to how CORS headers were for settings rules for HTTP requests made between
origins, security headers set rules for all HTTP requests.
Some of the headers that Masonite has built in when using the
SecureHeadersMiddleware middleware include
• Strict-Transport-Security
• X-Frame-Options
• X-XSS-Protection
• X-Content-Type-Options
• Referrer-Policy
• Cache-Control
• Pragma
These are pretty cryptic, so let’s try to go over each one and explain what they are
for. I won’t go over what each value means since there are many of them. I’ll just explain
what each option is there for, and you can do the research on what values you need to set
for your situation.
Meaning of the Headers
The Strict-Transport-Security header tells browser that it should make requests over
HTTPS and not HTTP. This is also known as an HSTS header ( HTTP Strict Transport
Security).
Chapter 6 Se Curity

118
The X-Frame-Options header tells a browser whether it should render pages inside