Django, InertiaJs and React - A guide to get you started - Pt. 1

saiforceone

Simon (Sai)

Posted on January 15, 2023

Django, InertiaJs and React - A guide to get you started - Pt. 1

⚠️ This guide is for versions of Inertiajs before v 1.0. I will be including the upgrade to InertiaJs V 1.0 in Part 2.

Motivations: I prepared this guide to solve a problem I was having with one of the projects that I have in production. I won't bore you with the details but, what I can say about it is that it's a Django project that makes use of ReactJs using Webpack but in a kind of thrown together way that made development a pain. With that in mind, I decided to search for a better way. Hopefully this will be of help to someone.

I'll be splitting this guide into two parts just to make it easier to consume.

Prerequisites

  • Some experience with the Django framework and React
  • Have an environment that is ready for Django app development (the usual requirements like Pip / Pipenv and so on)
  • Python 3.7+
  • NodeJS 16.x+
  • React Dev Tools

Tested platforms

  • Ubuntu 22.04 LTS
  • MacOS
  • Windows ** Thanks to @TappMax for testing

What we'll be doing in Part. 1

  • Setting up our Django project
  • Setting up GIT (optional, but usually a good idea)
  • Installing the required Django dependencies and configuring them
  • Setting up our Inertia App
  • Testing Django with our InertiaJS App

Initial Setup of up our Django Project

For this guide, we will be building a simple contact list manager. Since the main focus is to walk through getting Django, InertiaJS, React with Vite to play nicely, we will be skipping things like Authentication and styling (for the most part).

Let's begin getting our Django project started

  • Let's create a folder for our project and navigate to it
mkdir contact-app
cd contact-app
Enter fullscreen mode Exit fullscreen mode

New project

  • Create your virtual environment using the following command (or your preferred way to create a virtual environment):
python3 -m venv .venv
Enter fullscreen mode Exit fullscreen mode
  • activate your newly created environment using:
source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Activating virtual environment

  • install Django 4.1 LTS using the command and it would be a good idea to upgrade pip in our virtual environment as well
pip install Django==4.1
pip install --upgrade pip
Enter fullscreen mode Exit fullscreen mode

Installing Django

ℹ️ Your terminal output might be different but as long as all the packages were installed properly, you can proceed to the next step

⚠️ A note about the settings file in the django project that was just created: the SECRET_KEY is something that should be kept safe. There are a few things that we can do to secure our settings file but we'll save that for another time.

  • Create our django project in our current directory
django-admin startproject contact_manager .
Enter fullscreen mode Exit fullscreen mode

ℹ️ If the command ran correctly, you will not see anything returned. You can always checking using the ls command

Image description

  • Let's make sure our application is working correctly
./manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Image description

ℹ️ Your terminal should show something similar. Don't worry about unapplied migrations warning. We'll fix those soon.

  • At this point, you should be able to navigate to the development server address.

  • We should be up and running at this point. The console will tell us that we have unapplied migrations...that's ok for now

Up and running

ℹ️ We should see the start page when we navigate to http://127.0.0.1:8000 as directed in the terminal output

# All the commands
python3 -m venv .venv
source .venv/bin/activate
pip install Django==4.1
pip install --upgrade pip
./manage.py runserver
Enter fullscreen mode Exit fullscreen mode
# Windows Notes: Provided by Max

# All the commands on windows
python -m venv .venv
.venv\Scripts\activate
pip install Django==4.1
pip install --upgrade pip
manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Setting up git (optional, but recommended)

This step is optional but recommended. If you have the development server run, go ahead and stop it.

⚠️ if you intend to follow this step, the assumption here is that you have experience setting up GIT. That said, if you don't, I gotchu. This link will help: https://www.atlassian.com/git/tutorials/install-git

  • Let's initialize our git repo for this project
git init
Enter fullscreen mode Exit fullscreen mode
  • You should see something that looks like
Initialized empty Git repository in PATH/TO/YOUR/PROJECT
Enter fullscreen mode Exit fullscreen mode
  • Let's get a git ignore file in place from now. Depending on your IDE or Editor, you may be able to generate a .gitignore file.
  • Create a file named .gitignore in the root of the project and copy and paste the following (this file is probably overkill for our purposes)
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# PyInstaller
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Minimal Django:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Minimal Virtualenv
.venv

# Jetbrains
.idea

# Minimal NodeJS
dist
node_modules

Enter fullscreen mode Exit fullscreen mode
  • With this in place, we can add all our files and then commit them
git add .
git commit -m 'initial commit'
Enter fullscreen mode Exit fullscreen mode

Installing Django Dependencies

In this section, we'll be installing inertia/django and django-vite and configuring our settings to make things work.

  • If you're using version control, let's go ahead and make a new branch from main
git checkout -b install-dependencies # or whatever you want to call it
Enter fullscreen mode Exit fullscreen mode

Image description

Installing Inertia/Django

For more information about the Inertia.js Django Adapter: https://pypi.org/project/inertia-django/

pip install inertia-django
Enter fullscreen mode Exit fullscreen mode

Image description

ℹ️ Your output should look something like what's shown above

Now would be a good time to open up the project in your IDE or editor. I'll be using Pycharm Professional but VSCode is fine too. And let's have a look at the project structure so far.

Image description

  • Next, we will update our INSTALLED_APPS in our settings.py file to inertia inertia
INSTALLED_APPS = [
  # django inertia apps,
  'inertia',
  # our project's apps,
]
Enter fullscreen mode Exit fullscreen mode
  • and then update our MIDDLEWARE in settings.py to include the inertia middleware inertia.middleware.InertiaMiddleware
MIDDLEWARE = [
  # django middleware,
  'inertia.middleware.InertiaMiddleware',
  # your project's middleware,
]
Enter fullscreen mode Exit fullscreen mode

Image description

ℹ️ Your settings.py should look something like the image above

Next, create a template file templates/base.html having the initial structure

<!DOCTYPE *html*>
<html *lang*="en">
  <head>
    <meta *charset*="UTF-8">
    <title>Title</title>
  </head>
  <body>
    {% block *inertia* %}{% endblock %}
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • To settings, add INERTIA_LAYOUT = 'base.html' to the end of the settings file

Image description

  • Update DIRS in TEMPLATES to include BASE_DIR / 'templates'
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'], # add this line
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
Enter fullscreen mode Exit fullscreen mode

Image description

ℹ️ The TEMPLATES section of your settings.py should look like this

Installing Django-Vite

pip install django-vite
Enter fullscreen mode Exit fullscreen mode

Image description

  • Once installed, add django-vite to our installed apps
INSTALLED_APPS = [
  # django apps,
  'django_vite',
  'inertia',
  # your project's apps,
]
Enter fullscreen mode Exit fullscreen mode
  • In the root of the project, we will need to create a package.json file by using npm
npm init -y
Enter fullscreen mode Exit fullscreen mode

Image description

  • Now we can install Vite 🚀
npm i -D vite
Enter fullscreen mode Exit fullscreen mode

Image description

  • For vite to work, we need to create a vite.config.js file in the root of our project with the following content
import { resolve }  from 'path';

module.exports = {
 plugins: [],
 root: resolve('./react-app'),
 base: '/static/',
 server: {
   host: 'localhost',
   port: 3000,
   open: false,
   watch: {
     usePolling: true,
     disableGlobbing: false,
   },
 },
 resolve: {
   extensions: ['.js', '.json'],
 },
 build: {
   outDir: resolve('./react-app/dist'),
   assetsDir: '',
   manifest: true,
   emptyOutDir: true,
   target: 'es2015',
   rollupOptions: {
     input: {
       main: resolve('./react-app/src/main.js'),
     },
     output: {
       chunkFileNames: undefined,
     },
   },
 },
};
Enter fullscreen mode Exit fullscreen mode

Image description

  • Update the file templates/base.html with the following content
{% load django_vite %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <!-- include vite hmr -->
     {% vite_hmr_client %}
     {% vite_asset 'src/main.js' %}
     <title>Title</title>
  </head>
  <body>
     {% block inertia %}{% endblock %}
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

ℹ️ we are referencing js/main.js, we will create this file eventually.

  • Update settings.py to include the necessary settings for Django Vite
# InertiaJS Related settings
INERTIA_LAYOUT = 'base.html'

# We need this for django form posting
CSRF_HEADER_NAME = 'HTTP_X_XSRF_TOKEN'
CSRF_COOKIE_NAME = 'XSRF-TOKEN'

# Where ViteJS assets are built.
DJANGO_VITE_ASSETS_PATH = BASE_DIR / "react-app" / "dist"

# If we should use HMR or not.
DJANGO_VITE_DEV_MODE = DEBUG

# we need this to get around cors issues
DJANGO_VITE_DEV_SERVER_HOST = '127.0.0.1'

# this is the default, but I'm leaving this here, so you know what to change if you want to run on a different port
DJANGO_VITE_PORT = 3000

# Name of our static files' folder (after called python manage.py collectstatic)
STATIC_ROOT = BASE_DIR / "static"

# Include DJANGO_VITE_ASSETS_PATH into STATICFILES_DIRS to be copied inside
# when run command python manage.py collectstatic
STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH]
Enter fullscreen mode Exit fullscreen mode

Image description

  • Let's create our react-app/dist folder which we will use eventually (Django's dev server will complain about it)
  • Let's create our entry point for what will become our React/InertiaJS app react-app/src/main.js
alert('Django Vite says hello world!');
Enter fullscreen mode Exit fullscreen mode
  • Update package.json to include dev and build scripts so that we can run vite
{
  "scripts": {
    "build": "vite build",
    "dev": "vite",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
}
Enter fullscreen mode Exit fullscreen mode
  • At this point, we should be able to run vite and we shouldn't see any errors in the terminal. That said, we still can't do anything useful yet

Image description

In our browser, we should able to access our js file

Image description

  • Let's add a file for our views and we'll call it... contact_manager/views.py 😏
from django.shortcuts import render


def index(request):
   return render(request, "base.html")
Enter fullscreen mode Exit fullscreen mode
  • Let's update our urls urls.py
from django.contrib import admin
from django.urls import path

# this was added
from contact_manager import views
# from contact_manager.views import index # this is a valid import as well

urlpatterns = [
   path('', views.index), # this was added
   path('admin/', admin.site.urls),
]
Enter fullscreen mode Exit fullscreen mode
  • Open a new terminal and run the django dev server
  • Navigate to http://127.0.0.1:8000 and we should see our JavaScript alert.

Django Vite says Hello World!

ℹ️ We have HMR working. Of course you can check that it works by changing the main.js file and observing what happens. 👍

Image description

  • Since we're at a decent point, let's commit our changes before we move forward (if you're using version control)
git add .
git commit -m "Install dependencies and did initial setup for vite to serve main.js file via vite"
Enter fullscreen mode Exit fullscreen mode

Now for the interesting part 🙌

Update the previously created contact_manager/views.py fileto include an import from inertia

from inertia import inertia


@inertia('Home/Index')
def index(request):
   return {
       'contacts': ['Acid Burn', 'Crash Override', 'Lord Nikon'],
   }
Enter fullscreen mode Exit fullscreen mode

Image description

ℹ️ The object we return from our index function becomes the props of the React component we will eventually render

If everything worked correctly, when we inspect our index page, we should see a div that has id="app" along with a data-page attribute that contains the contacts as defined in our python view file.

Image description

The last thing we'll do for Part 1 of this tutorial is to setup our InertiaJS application

  • Let's install the dependencies we need via NPM
npm i -D react react-dom @vitejs/plugin-react
Enter fullscreen mode Exit fullscreen mode

Edit: For our InertiaJs dependencies @inertiajs/inertia-react @inertiajs/progress, we will need specific versions as shown in the package.json snippet below.

"devDependencies": {
    "@inertiajs/inertia-react": "^0.8.1",
    "@inertiajs/progress": "^0.2.7",
    "@types/react": "^18.0.26",
    "@types/react-dom": "^18.0.10",
    "@vitejs/plugin-react": "^3.0.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "ts-loader": "^9.4.2",
    "typescript": "^4.9.4",
    "vite": "^4.0.4"
  }
Enter fullscreen mode Exit fullscreen mode

Image description

  • Stop vite if you have not yet done so

  • Rename our main.js to main.jsx and replace the content with what is shown below

import React from 'react';
import { createRoot } from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/inertia-react';
import { InertiaProgress } from '@inertiajs/progress';
import Layout from './components/Layout.jsx';

const pages = import.meta.glob('./pages/**/*.jsx');

document.addEventListener('DOMContentLoaded', () => {

 InertiaProgress.init();

 createInertiaApp({
   resolve: async (name) => {
     const page = (await pages[`./pages/${name}.jsx`]()).default;
     page.layout = page.layout || Layout;

     return page;
   },
   setup({ el, App, props }) {
     createRoot(el).render(<App {...props} />);
   },
 }).then();
});
Enter fullscreen mode Exit fullscreen mode
  • Let's update our vite.config.js file to work with our changes
import { resolve }  from 'path';
import react from '@vitejs/plugin-react';

module.exports = {
 plugins: [
   react({
     include: '**/*.disabled',
   }),
 ],
 root: resolve('./react-app'),
 base: '/static/',
 server: {
   host: 'localhost',
   port: 3000,
   open: false,
   watch: {
     usePolling: true,
     disableGlobbing: false,
   },
 },
 resolve: {
   extensions: ['.js', '.json'],
 },
 build: {
   outDir: resolve('./react-app/dist'),
   assetsDir: '',
   manifest: true,
   emptyOutDir: true,
   target: 'es2015',
   rollupOptions: {
     input: {
       main: resolve('./react-app/src/main.jsx'),
     },
     output: {
       chunkFileNames: undefined,
     },
   },
 },
};

Enter fullscreen mode Exit fullscreen mode
  • Let's create a layout component src/components/Layout.jsx which we will use to wrap our pages
const Layout = ({ children }) => {
 return (
   <main>
     <div>{children}</div>
   </main>
 );
};

export default (page) => <Layout>{page}</Layout>;
Enter fullscreen mode Exit fullscreen mode
  • Update templates/base.html to point to our main.jsx file
  • Create our Index page component src/pages/Home/Index.jsx and paste the following content
export default function Home ({contacts}) {
 return (
   <div>
     <h1>Contact List</h1>
     {Array.isArray(contacts) && contacts.length ? <ul>
       {contacts.map(contact => <li key={contact}>{contact}</li>)}
     </ul> : <p>No contacts yet...</p>}
   </div>
 );
}

Enter fullscreen mode Exit fullscreen mode

ℹ️ Now, if everything worked, we should see something that looks like the image below 👍

Image description

  • Go ahead and commit our changes
git add .
git commit -m "update implementation to render inertia js app with props"
Enter fullscreen mode Exit fullscreen mode

If you've made it this far and everything works then, nice! As you can see, InertiaJs is pretty cool and being able to use it with Django makes it way cooler (imo).

Coming up in part 2, we'll take a look at the following:

  • Upgrade InertiaJs to v1.0
  • Integrating TypeScript
  • Installing Material UI (you can use any component library you want)
  • Implementing the contact app
    • Backend
    • models, views
    • Frontend
    • Pages and components

Hope you found this guide useful. Let me know if you have questions or suggestions.

References:

The following links were used in the making of this guide

💖 💪 🙅 🚩
saiforceone
Simon (Sai)

Posted on January 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related