Masonite Framework Tutorial Series Part 4 - Views
Tags: python, framework, programming, masonite
Published: 2018-07-15 18:34:20

---
title: Masonite Framework Tutorial Series Part 4 - Views
published: true
description: Part 4 of the Python framework tutorial series. Talking about Views!
tags: python, framework, programming, masonite
---

# Introduction

[Masonite](http://github.com/masoniteframework/masonite) uses a traditional MVC (Model-View-Controller) architecture. While most frameworks use this architectural pattern, each framework subjectively interprets it differently. We have already gone over the C (Controller) in one of the previous tutorials so now we are going to talk about the V (View).

If you are coming from a framework like Django then you may be used to views being Python code like a function (function based views) or a Python class (class based views) but in Masonite, a view is simply just an HTML template. Throughout the documentation and possibly these tutorials, the words “Templates” and “Views” are used interchangeably.

Since views are one of the main parts of your application, and you will have many of them, Masonite makes them very easy to use. For the basics, views are very simple but they can be as complex as you need them to be. Let’s just dive in and explain as many things in as much detail as possible.

# Returning Views

The most common use case for views on the backend if going to be returning them and passing in data to them. If you are coming from any framework then this should make a lot of sense to you. We already explained how to return views in the previous tutorial but for a quick refresher it will like this:

```python
def show(self):
    return view('template', {'key': 'value'})
```

So this is really important to note. Notice here we are using a builtin function here. This structure will come with the `WelcomeController` out of the box so at first glance this may seem weird. 

These are called [builtin helper functions](https://docs.masoniteproject.com/the-basics/helper-functions) (because they utilize Python builtins) and are designed to allow you to rapidly build up prototypes where you can later go in and refactor or leave them. I personally refactor after everything is working. If you don’t like having builtins inside your application and you think it is too much “Magic” then you can remove the `HelpersProvider` in your `PROVIDERS` list. All builtin helper functions are added tot he application through that provider.

If you don’t like the builtin helper functions then you can also resolve through the controller parameters:

```python
def show(self, View):
    return View('template', {'key': 'value'})
```

If this right here is a little confusing then be sure to read the documentation on the [Service Container](https://docs.masoniteproject.com/architectural-concepts/service-container) and how the IOC and dependency resolver works.

This is exactly the same as using the helper function above but this is resolved using the [Service Container](https://docs.masoniteproject.com/architectural-concepts/service-container) instead. If you are not familiar with how the [Service Container](https://docs.masoniteproject.com/architectural-concepts/service-container) resolves things then be sure to read the documentation here.

The object resolved is actually just the render method on the View class. If you want to be a super awesome Pythonista and stick to importing the things you need then you can resolve by using Python annotations:

```python
from masonite.view import View

def show(self, view: View):
    return view.render('template', {'key': 'value'})
```

Notice that we are using the `view.render` method here. As stated previously, the `View` key was just an alias to this render method on the View class.

How you return views is completely up to you and your team. Make decisions as you see fit. I personally use the last option and import all my classes but it is entirely up to you.

# Templates
The first parameter used when returning views is always the template. All templates are located in the `resources/templates` directory. So if we returned a view like this:

```python
from masonite.view import View

def show(self, view: View):
    return view.render('index', {'key': 'value'})
```

It will look for the `resources/templates/index.html` file and render that.

Most templates will be able to be located inside the `resources/templates` directory without issue and you can obviously go as many directories deep as you need such as a template like:

```python
view.render('dashboard/users/settings/edit', ...)
```

This will look at the `resources/templates/dashboard/users/settings/edit.html` file.

# Global Templates

Some templates can be rendered from third party packages or from other directories. For example we could do something like `pip install invoices` and then may need to return a view for invoices:
 
```python
view.render('/invoices/templates/show', ...)
```

Notice the preceding forward slash. This signals to Masonite that you should start looking for that template in that module. This would look inside the `invoices` module and then render a `templates/show` file. 

This doesn’t only work for just third party packages but any modules in your application as well. If we put all of our templates inside the `storage` directory that comes with Masonite then we can specify our templates from that module:

```python
view.render('/storage/templates/index', ...)
```

## Passing In Data

Notice above we had a dictionary with a key value pair. This is the information that will be available in our template. If you are coming from any framework ever then this will be common sense to you.

We will be passing in data that we make from our controller into our view and then it is up to our view to show that data. For example we might have something like:

```python
from masonite.view import View
from app.User import User

def show(self, view: View):
 	  user = User.find(1)
    return view.render('index', {'user': user})
```

And inside our `resources/templates/index.html` file we can do something like:

```html
{{ user.email }}
```

If the curly brackets don’t make much sense yet don’t worry. Masonite uses Jinja2 which we will go into more detail with in a little bit. If you are familiar with Jinja2 then you should be good. If not then this is just how we display data in the browser. Jinja2 will take these syntax semantics and then parse them with the data we passed to it.

# Jinja2 Language

If you are familiar with Jinja2 then you can really skip this section. There is nothing special here. If you are not then you can keep reading.

In this section we will really just go over everything you NEED to know and all the extra fluff related to [Jinja2](http://jinja.pocoo.org/docs/2.10/) you can head on over to their documentation pages.

## Displaying Data

Like previously said, we can pass data into our view and display it using the double curly brackets. The above code would output something like:

```html
your-email@gmail.com
```

Theres really not much to displaying data besides filtering which we will talk about in a bit. 

Below are the parts of [Jinja2](http://jinja.pocoo.org/docs/2.10/) templating that you NEED to know.

## If Statements

We can use some logic in our templates simply by using if statements:

```html
{% if user.email == 'joe@email.com' %}
    Hello Joe
{% elif user.email == 'bill@email.com' %}
    Hello Bill
{% else %}
    I don't know you
{% endif %}
```

## For Loops
```html
{% for key in keys %}
    {{ key }}
{% endfor %}
```

## Extending Views

We can “extend” our view so we can have a base template where all of our logic inherits from. If you have used any template language before then this will make a lot of sense to you:

```html
{% extends 'nav/base.html' %}

{% for key in keys %}
    {{ key }}
{% endfor %}
```

This will inject the `nav/base.html` template at the top and inject the code underneath it.

## Template Blocks

We can use template blocks as placeholders for various information we will use later on. Our base template might look like this:

```html
<!-- nav/base.html -->
<html>

<head> 
  {% block css %}{% endblock %}
</head>

<body>
<h1> Hey! </h1>
{% block content %}{% endblock %}
<h2> Hope to see you again soon! </h2>
</body>
</html>
```

And then our children templates can look like this:

```html
<!-- dashboard/user.html -->
{% extends 'nav/base.html' %}

{% block css %}
  <link href="/static/style.css">
{% endblock %}

{% block content %}
Some awesome content
{% endblock %}
```

Which will then render a final template to the browser that looks like:

```html
<!-- nav/base.html -->
<html>

<head> 
  <link href="/static/style.css">
</head>

<body>
<h1> Hey! </h1>
Some awesome content
<h2> Hope to see you again soon! </h2>
</body>
</html>
```

## Including Files

We can also include files as well so this could be something like a sidebar where all the logic can be kept in a single template for that sidebar. This can look something like:

```html
<!-- nav/base.html -->
<html>

<head> 
  {% block css %}{% endblock %}
</head>

<body>
<div class="col-xs-4">
  {% include 'snippets/sidebar.html' %}
</div>
<div class="col-xs-8">
  {% block content %}{% endblock %}
</div>

</body>
</html>
```

You can include templates anywhere you like. All included templates inherit all the variables from the current template.

If your included template needs variables that will not be present in the templates that it is included in then consider using the View Sharing feature.

## Using Filters

Jinja2 comes with what they call “filters” which are really just functions that receive the variable it is attached to and then returns that value. 

You can apply a filter to a variable with the pipe character. For example:

```html
{{ user.email|striptags|title }}
```

Again these are built in and can be used immediately. A list of filters you can use can be found on the Jinja2 documentation site. There are way too many to list here. 

## Building Filters

For more advanced things that are application specific, we can build our own filters. If you are not familiar with [Service Providers](https://docs.masoniteproject.com/architectural-concepts/service-providers) then you should read about those first.

In order to add filters to all of our templates we can just use the View class. The best place to put this code is inside a [Service Provider](https://docs.masoniteproject.com/architectural-concepts/service-providers) whose `wsgi` attribute is set to `False`. This will ensure that the filter isn’t being added to our codebase over and over again and slow down our code. If `wsgi` is `False` then it will only be loaded into the View class when the server first starts.

You should create a [Service Provider](https://docs.masoniteproject.com/architectural-concepts/service-providers) specifically for all of your filters but for now we will just use the `UserModelProvider` and just stick the code in there. In order for this to work we just need to add a function to the View class. We can bind filters to the View class by using the `filter` method:

```python
from masonite.view import View

def split_string(variable):
    return variable.split(',')

class UserModelProvider(ServiceProvider):

    wsgi = False

    ...

    def boot(self, Request, view: View):
        view.filter('split', split_string)
```

That’s it! We just needed to bind a function to the View class. Now we can use it in our template:

```html
{{ user.email|split }}
```

# Helper Functions

Just like we have some helper functions in our backend code, we have some helper functions already injected into our template for us.

## Request

We can get the current request object easily:

```python
{{ request().path }}
```

This is just a request class injected into the view.

## Current User
We can get the current user:

```python
{{ user().email }}
```

## Session
This contains the Session class:

```python
{{ session().get('key') }}
```

## Getting Routes

We can get any named route we have:

```python
{{ route('route.name') }}
```

This will return something the URL for the route name. If your route URL is something like `/route/name/@id` then you’ll have to specify the route parameter as the second parameter:

```python
{{ route('route.name', {'id': 1}) }}
```

## Request Method

Masonite supports all forms of request methods to include `PUT`, `PATCH`, `DELETE` etc. The problem is that HTML forms only accept `GET` and `POST`. We can mock the request method by using a helper method:

```html
<form action="{{ route('route.name') }}">
    {{ request_method('PUT') }}
</form>
```

When submitted this will submit the form as a `PUT`.

## Going Back

After submitting a form we may want to redirect back. This could be because of a failed form or incorrect validation or something.

```html
<form action="{{ route('route.name') }}">
    {{ back(request().path) }}
</form>
```

This will redirect back to the current route because we specified the current path to go back to. We could also specify a route using the route helper from before:

```html
<form action="{{ route('route.name') }}">
    {{ back(route('form.errors')) }}
</form>
```

The way this is used is inside the controller:

```python
def show(self):
    if some error:
        request().back()
    
    do some other logic here
```

Masonite will know where we want to go back to because of the `__back` input that is submitted using the `back` helper.

# Other Helpers
## CSRF Protection.
All forms submitted that are not a `GET` request are protected by CSRF attacks. We always need to specify the CSRF token on these types of requests:

```html
<form action="{{ route('route.name') }}" method="POST">
    {{ csrf_field|safe }}
</form>
```

If you forget one then you will receive an exception saying something to the effect of an invalid or missing CSRF token.

In templates. I want to start out this tutorial talking about helper functions

# View Class
The view class itself is the foundation that all views are handled. Because of this, anything related to the views such as adding helper functions, rendering, adding filters, environments, etc., are part of the view class.

This class is loaded into the container with the `ViewClass` alias. Most binding of objects to the view class should be done inside a [Service Provider](https://docs.masoniteproject.com/architectural-concepts/service-providers) where the `wsgi` attribute is `False` to avoid the application from slowing down.

## Getting the View class.
Like previously states, the View class is bound to the container using the `ViewClass` alias so we can resolve the class like this:

```python
    def boot(self, Request, ViewClass):
        ViewClass.share(..)
```

Or by annotation resolving:

```python
from masonite.view import View
...
    def boot(self, Request, view: View):
        view.share(..)
```


## Sharing

If you want to share a variable or function or something with all of your templates then we use the `share()` method.

This takes a dictionary. The key is what will be used in the template and the value will returned.

```python
from masonite.view import View
...
    def boot(self, Request, view: View):
        framework = 'Masonite'
        view.share({'framework': 'Masonite'})
```

Now we can use that in all template:

```html
{{ framework }} <!-- "Masonite" -->
```

## Composing

Slightly different from sharing we can use View Composing.  This is essentially sharing but only for specific templates. Let’s say we have a template structure like this:

```
resources/
  templates/
    dashboard/
      show.html
      user.html
      base.html
    settings/
      show.html
      index.html
```

If you only want a specific variable to be available in only the dashboard `user.html` template then we can compose:

```python
from masonite.view import View
...
    def boot(self, Request, view: View):
        framework = 'Masonite'
        view.compose('dashboard/user': {'framework': 'Masonite'})
```

The first argument is the template and the second argument is the key value pair you want available. This will work the same as view sharing but only for that specific template.

You can also specify a wildcard of templates:

```python
from masonite.view import View
...
    def boot(self, Request, view: View):
        framework = 'Masonite'
        view.compose('dashboard/*': {'framework': 'Masonite'})
```

This will make that key value pair available in all the dashboard templates to include `dashboard/show.html`,  `dashboard/user.html`,  `dashboard/base.html`.

## Environments

Environments can be thought of simply as locations of templates. Out of the box, Masonite only comes with 1 template environment (`resources/templates`) which only needs the base directory to work but you can easily load other environments in:

```python
from masonite.view import View
...
    def boot(self, Request, view: View):
        view.add_environment('invoices/templates')
```

How this works is that it looks inside the `invoices` module for the templates directory. This is fantastic for using third party packages as well since the module doesn’t need to be in your file structure but can be in your Python environment.

Take for example this scenario where you install a package called invoices and need to add new template environments to your application. 

You can do something like:

```
$ pip install masonite-invoices
```

Follow the instructions on adding the package to your `PROVIDERS` list and then we can have access to all of that packages views:

```
{% extends 'invoices/base.html' %}

{% include 'my/application/snippet.html' %}
```

Notice we are now combining multiple template environments where the first line is coming from the `masonite-invoices` package and the bottom line is coming from our application.

****

Thanks for reading! Be sure to give a star on the [Masonite GitHub Repo](http://github.com/masoniteframework/masonite) page or join the [Official Masonite Slack Channel](http://slack.masoniteproject.com) if you have any questions!