How to build a django app with stimulus and turbolinks
Tags: python, javascript, webdev, django
Published: 2020-05-24 15:28:05

# How to build an App with Django, Turbolinks and Stimulus
### TL;DR
So you want to have a little starter? Great because I built one for me [here](https://github.com/webspaceadam/django-stimulus-turbolinks-starter) with this tutorial :)

## A real-world problem
I started to work on my Bachelor Thesis a couple of weeks ago. I'm building a Recommender System for a B2B-Application and needed a dashboard to show some stats and information. In the last one and a half years I mainly worked on React-Applications, lately some Next.Js. But I didn't want to blow my whole Django application with an additional react application, because that meant I needed to implement a fully-featured rest-API for every information stored in the database. But what I wanted to have is the feeling of a single page application. I knew, that the good guys at [basecamp](https://basecamp.com/) built something like that. And that thing was a combination of turbolinks and stimulus. I always wanted to try this different approach to building modern web applications. But I never had a real "opportunity" for this. So now was the time. 

## Techstack
Another part of my seeking of knowledge was the implementation of webpack in a Django app. I never used it before in Django applications, because I only used Django to create rest API's for my machine learning projects. And they had a standalone react-frontend built with create-react-app (terrible, I know). 
So here are the parts we will have in our Application:
- Webpack
- Turbolinks
- Stimulus
- Django (managed with pipenv)

## Starting the environment and installing the python dependencies
Start by creating a folder. You can call it how you want it, but i will use the name **django-stimulus-turbolinks-starter**. 
All my non-jupyter python projects start with the simple usage of:

```
pipenv install --python=3.7
```

So now we have our basic python environment. Who does not know pipenv, you could describe it like the npm of the python world. Check it out [here](https://pipenv.pypa.io/en/latest/)

After the successful creation of our environment, we want to start the shell so we do not need to specify the python version we're using:

```
pipenv shell
```

At this point we start to work in our virtual environment. The perfect moment for the installation of Django. 

```
pipenv install django
```

When the installation is finished, we want to start a new django project:

```
django-admin startproject dsts . 
```

The dot at the end means that we want to start the app in the current directory. The "dsts" just is short for "django stimulus turbolinks starter".

I almost always add the first application after this point:

```
django-admin startapp dashboard
```

I called it dashboard because that was the name of the first app i used in the code of my thesis. 

So far, so good! We set up a basic little django application. At this point we will leave the python world to start implementing the javascript side of this project. 

##  Initializing and setting up the npm-project
So npm. My first love of package managers. And honestly it's still the best I know. No doubt. So here we start with the well-known command: 

``` 
npm init -y
```

To speed things up, I added the *-y* Flag. In case you don't know what the y-flag is doing, read it up [here](https://docs.npmjs.com/creating-a-package-json-file#creating-a-default-packagejson-file).

As the next step we need to install our dependencies. I added some more than only the packages we really **need**. So we have some additional file loading, babel, and so on. But first to the most important part: 

```
npm install webpack webpack-cli stimulus turbolinks css-loader file-loader --save
```

But we need some additional packages for our dev-dependecies:

```
npm install --save-dev mini-css-extract-plugin nodemon @babel/core @babel/plugin-proposal-class-properties @babel/preset-env babel-loader webpack-dev-server
```

And that's it! We have the most important parts of our application ready to use.

## Creating and changing the config files
To use webpack correctly in the application we need to create a config file. In this config file we can implement all our wishes for the bundling. I found [this](https://youtu.be/TzdEpgONurw) tutorial about webpack pretty useful. In the chapter above we added MiniCssExtract as a dependency. We did this, to get an additional CSS file through webpack, so not every style will be added into the header of our site. Additionally to the webpack config file we need to make some changes to the *package.json* and the *settings.py* of our django app. But let us start with the webpack config. 

### The Webpack Config file

```
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: 'development',
    entry: './static/js/index.js',
    plugins: [new MiniCssExtractPlugin()],
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader'],
            },
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                        plugins: [
                            '@babel/plugin-proposal-class-properties'
                        ]
                    }
                }
            }
        ]
    }
}
```

As you can see we currently just have rules for CSS and javascript. To get more information I recommend watching the tutorial I linked above. 

### package.json
In this file we will only add two lines of code to the script section. We will add a *build* and *start* command.

```
"scripts": {
    "start": "nodemon -w webpack.config.js -x webpack-dev-server",
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
```

To be sure we will use the *npm run build*-command right away, to create the *dist* folder. 

### settings.py

At the beginning of this article we created the first application, so I will use this moment to add it to our installed apps section in the *settings.py*. If you want to learn more about django I recommend the tutorials of [Corey Schafer](https://youtu.be/UmljXZIypDc). He is, in my opinion, the best python teacher out there. His content is free I and always found an answer to a question I had. 


```
INSTALLED_APPS = [
    // just add it at the end of this list
    'dashboard'
]

// at the bottom of the file
STATIC_URL = 'http://127.0.0.1:8080/' # for webpack dev
STATICFILES_DIRS = ['dist'] # for the generated dist folder
```

And that's our tech stack!

## The index.js, index.css and the first stimulus controller
To get the app fully working together we need to add some additional folders. At the static-side of things we need to create a *static* folder in the root directory. We already have seen where it will go, in the *webpack.config.js* And in our app *dashboard* we'll create a folder called "templates". 

So this will look something like this:

```
django-stimulus-turbolinks-starter
|    dashboard
|    |    templates 
|    static
|    |    css
|    |    |    index.css
|    |    js
|    |    |    controllers
|    |    |    |    hello_controller.js
|    |    |    index.js
```

### index.js

```
import { Application } from 'stimulus';
import { definitionsFromContext } from 'stimulus/webpack-helpers';
import Turbolinks from 'turbolinks';

// import css
import './../css/index.css'

const application = Application.start();
const context = require.context('./controllers', true, /\.js$/);
application.load(definitionsFromContext(context));
Turbolinks.start();
```

### index.css

```
.content {
    max-width: 300px;
    margin: auto;
  }

p {
    font-size: 32px;
}
```

Here we create our **Stimulus** and **Turbolinks** Application and added some super basic styling. I hope that this code is self-explanatory. 

### static/controllers/home_controller.js
```
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name", "output" ]

  greet() {
    this.outputTarget.textContent =
      `Hello, ${this.nameTarget.value}!`
  }
}
```

This is the same controller you can see at the Stimulus homepage. I won't go deeper into stimulus in this tutorial. If you want to learn more, I recommend reading the [documentation](https://stimulusjs.org/handbook/introduction). 

### templates/home.html

```
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="{% static 'main.js' %}"></script>
    <link rel="stylesheet" type="text/css" href="{% static 'main.css' %}">
    <title>Django Stimulus Turbolinks Starter</title>
</head>
<body>
    <div data-controller="hello" class="content">
      <p>
        Hello Stimulus!
      </p>
        <input data-target="hello.name" type="text">
      
        <button data-action="click->hello#greet">
          Greet
        </button>
        
        </br>
        <span data-target="hello.output">
        </span>
      </div>
</body>
</html>
```

This is the first moment we see a django template together with a stimulus controller. And surprise surprise, doesn't look unfamiliar, right? That's because we don't need to add any javascript at this place. Pretty neat!

## Serving the template and starting the application
Congratulations! This is the last step of this tutorial :)

As the last step we need some additional lines of code in our django code to fully serve our django-stimulus-turbolinks application. 

### dashboard/views
```
from django.shortcuts import render
from django.views.generic import TemplateView

# Create your views here.
class LandingView(TemplateView):
    template_name = 'home.html'
```

### dsts/urls

```
from django.contrib import admin
from django.urls import path
from dashboard.views import LandingView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', LandingView.as_view(), name='home')
]
```
And that's it!

Now you can use the following commands to start the application and begin the development of your django-stimulus-turbolinks application!

Build!
```
npm run build
```

Start it!
```
python manage.py runserver
```
## Roundup
Whoop whoop! You did it. You created an application that is built on django, stimulus, and turbolinks. I hope this tutorial was helpful and you learned something.