loading...
Cover image for Django and Vue 3.0 with Webpack Loader and typescript support

Django and Vue 3.0 with Webpack Loader and typescript support

clifinger profile image Julien Lenne ・6 min read

Integrating Django as a backend to a running Vue App.

The beta version of Vue 3 has been available for some time now!
While I’m writing this post the beta 10 version has just been released.

Let’s begin.
I am assuming that you have already the powerful version manager asdf installed. And that you have basic understanding of Django and Nodejs package handling.

Create a virtual environment with Nodejs, yarn, python 3, PostgreSQL, Django and create a Django Application

You will need a compiler.
Ubuntu:

  • sudo apt install linux-headers-$(uname -r) build-essential

On Ubuntu, you will need libreadline

  • sudo apt-get install libreadline-dev

On Ubuntu 20.04, you will need curl and zlib

  • sudo apt-get install zlib1g-dev curl
asdf plugin-add nodejs
asdf plugin-add yarn
asdf plugin-add python
asdf plugin-add postgres

# Import the Node.js release team's OpenPGP keys to main keyring:
bash ~/.asdf/plugins/nodejs/bin/import-release-team-keyring

# create virtual env for all
mkdir venv-django
cd venv-django

# install all latest versions
~/venv-django asdf install nodejs latest
~/venv-django asdf install yarn latest
~/venv-django asdf install python latest
~/venv-django asdf install postgres latest

# set .tool-versions in your virtual env
# if you don't remember the latest version you can use  
# `asdf list postgres` for example
~/venv-django asdf local nodejs 14.2.0
~/venv-django asdf local yarn 1.22.4
~/venv-django asdf local python 3.8.2
~/venv-django asdf local postgres 12.2

# check if all good
cat .tool-versions

~/venv-django node -v
v14.2.0

~/venv-django yarn -v
1.22.4

~/venv-django python --version
Python 3.8.2

~/venv-django postgres --version
postgres (PostgreSQL) 12.2

# start postgres
pg_ctl start

# create database
~/venv-django createdb django

#test connection
~/venv-django psql -d django
django=# exit

Now, you should be ready to create the project backend:

~/venv-django pip install django

# reshim v-env
~/venv-django asdf reshim

# create Django project
~/venv-django django-admin startproject mysite
~/venv-django cd mysite

Configure django to use postgres

~/venv-django pip install psycopg2-binary
# mysite/mysite/settings.py

. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django',
        'USER': '',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '',
    }
}

. . .

Run migrations

~/venv-django/mysite python manage.py migrate

test backend server: ~/venv-django/mysite python manage.py runserver and navigate to http://127.0.0.1:8000/

At this point, you should see:
Django default page

Hit CONTROL-C, and now, you should be ready to create the frontend project:

~/venv-django/mysite mkdir frontend && cd frontend

# init the frontend project and hit enter for all questions
~/venv-django/mysite/frontend yarn init
yarn init v1.22.4
question name (frontend): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private: 
success Saved package.json
Done in 3.56s.

Install the dependencies:

~/venv-django/mysite/frontend yarn add vue@3.0.0-beta.10 webpack-bundle-tracker@0.4.3
~/venv-django/mysite/frontend yarn add --dev yarn vue-loader@v16.0.0-alpha.3 webpack-cli webpack webpack-dev-server typescript ts-loader @vue/compiler-sfc@v3.0.0-beta.10

Create a webpack.config.js in your frontend directory:

// frontend/webpack.config.js

const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const BundleTracker = require('webpack-bundle-tracker');

module.exports = (env = {}) => {
  return {

    mode: env.prod ? 'production' : 'development',
    devtool: env.prod ? 'source-map' : 'cheap-module-eval-source-map',
    entry: path.resolve(__dirname, './src/main.ts'),
    output: {
      path: path.resolve(__dirname, './dist'),
    },
    module: {
      rules: [
        {
          test: /\.vue$/,
          use: 'vue-loader'
        },
        {
          test: /\.ts$/,
          loader: 'ts-loader',
          options: {
            appendTsSuffixTo: [/\.vue$/],
          }
        },
      ]
    },
    resolve: {
      extensions: ['.ts', '.js', '.vue', '.json'],
      alias: {
        'vue': '@vue/runtime-dom'
      }
    },
    plugins: [
      new VueLoaderPlugin(),
      new BundleTracker({
        filename: './webpack-stats.json'
        publicPath: 'http://0.0.0.0:8080/'
      })
    ],
    devServer: {
      headers: {
        "Access-Control-Allow-Origin":"\*"
      },
      public: 'http://0.0.0.0:8080',
      inline: true,
      hot: true,
      stats: "minimal",
      contentBase: __dirname,
      overlay: true
    }
  };
}

we need to add a tsconfig.json file with the following rules in the frontend directory:

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "declaration": false,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "module": "es2015",
    "moduleResolution": "node",
    "noImplicitAny": false,
    "noLib": false,
    "sourceMap": true,
    "strict": true,
    "strictPropertyInitialization": false,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es2015",
    "baseUrl": "."
  },
  "exclude": [
    "./node_modules"
  ],
  "include": [
    "./src/**/*.ts",
    "./src/**/*.vue"
  ]
}

Add the launch command to package.json:

{

. . .


"scripts": {
    "serve": "webpack-dev-server"
  }
}

Create our vue 3 app:

~/venv-django/mysite/frontend mkdir src/ && cd src/

# Entry file
~/venv-django/mysite/frontend/src touch main.ts

#ts definitions for *.vue files
~/venv-django/mysite/frontend/src touch shims-vue.d.ts

# Default vue 3 component
~/venv-django/mysite/frontend/src touch App.vue

main.ts:

// frontend/src/main.ts

import {createApp} from 'vue';
import App from './App.vue';

createApp(App).mount('#app');

shims-vue.d.ts to avoid error with *.vue files:

// frontend/src/shims-vue.d.ts

declare module "*.vue" {
    import { defineComponent } from "vue";
    const Component: ReturnType<typeof defineComponent>;
    export default Component;
}

And finally our component App.vue:

<!-- frontend/src/App.vue -->


<template>
  <h2>This is a Vue 3 component!</h2>
  <button @click="increase">Clicked {{ count }} times.</button>
</template>
<script lang="ts">
import {defineComponent, ref} from "vue";
export default defineComponent({
  setup() {
    const count = ref(0)
    const increase = () => {
      count.value++
    }

    return {
      count,
      increase,
    }
  }
});
</script>

Test the project

yarn serve          
yarn run v1.22.4
$ webpack-dev-server
ℹ 「wds」: Project is running at http://0.0.0.0:8080/
ℹ 「wds」: webpack output is served from http://0.0.0.0:8080/
ℹ 「wds」: Content not from webpack is served from /home/julien/venv-django/mysite/frontend
ℹ 「wdm」:    44 modules
ℹ 「wdm」: Compiled successfully.

Hit CONTROL-C and Congratulation now you have frontend and backend !!!
But sorry, they are not link yet, let's do it:

~/venv-django/mysite/frontend/src cd ../../

# Install webpack loader
pip install django-webpack-loader==0.6.0

Let's configure Django by setting the frontend dir in settings.py:

# mysite/mysite/settings.py

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# ADD THESE TWO LINES
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
FRONTEND_DIR = os.path.join(BASE_DIR, 'frontend')

. . .

Add webpack_loader to your installed modules in settings.py:

# mysite/mysite/settings.py

. . .

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'webpack_loader',
]

. . .

Add TEMPLATES_DIR to your templates' config in settings.py:

# mysite/mysite/settings.py

. . .

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [TEMPLATES_DIR, ],
        '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',
            ],
        },
    },
]

. . .

Add webpack-loader config at the end of the settings.py:

# mysite/mysite/settings.py

. . .

WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': DEBUG,
        'BUNDLE_DIR_NAME': '/bundles/',  # must end with slash
        'STATS_FILE': os.path.join(FRONTEND_DIR, 'webpack-stats.json'),
    }
}

Create templates' directory:

~/venv-django/mysite mkdir templates
~/venv-django/mysite touch templates/application.html

Create main template inside:

<!-- mysite/templates/application.html -->

{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>Django Vue Integration</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
  </head>
  <body>
    <noscript>
      <strong>We're sorry but frontend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
<div id="app">
     <app id="inspire"></app>
 </div>
{% render_bundle 'main' %}
<!-- built files will be auto injected -->

</body>
</html>

And finally add the route in urls.py

# mysite/mysite/urls.py

from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path("",
         TemplateView.as_view(template_name="application.html"),
         name="app",
         ),
]

Now you can start the apps in two different tabs:

~/venv-django/mysite/frontend yarn serve                                             
yarn run v1.22.4
$ webpack-dev-server
ℹ 「wds」: Project is running at http://0.0.0.0:8080/
ℹ 「wds」: webpack output is served from http://0.0.0.0:8080/
ℹ 「wds」: Content not from webpack is served from /home/julien/venv-django/mysite/frontend
ℹ 「wdm」:    44 modules
ℹ 「wdm」: Compiled successfully.
~/venv-django/mysite python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
May 09, 2020 - 14:48:49
Django version 3.0.6, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

navigate to http://127.0.0.1:8000/

You should see:
Django default page

Congratulation and enjoy !!!

Tips:

Posted on by:

Discussion

pic
Editor guide