DEV Community

Jordan Engstrom
Jordan Engstrom

Posted on • Edited on

Hooking up Django and Vue.js

Alt Text

This article is for individuals who have had intermediate practice with the Vue.js and Django separately, but need direction on how to make these two frameworks work together. I will outline an example of how to set up some boilerplate code with a straightforward project structure using Django and Vue.js. Please note I am developing on macOS Catalina, so the shell commands will vary for your operating system. The finished product can be found on my github: https://github.com/jordanengstrom/blank_django_vue_project

At a high level we will be aiming for a project structure that looks something like this:

my_project/
|
|____ core/
|    |____ __init__.py
|    |____ views.py           # class based TemplateView for index.html
|    
|
|____ frontend/               # root frontend
|    |____ src/               # vue components, router, store, etc.
|    |____ node_modules/
|    |____ vue.config.js      # important file number 1
|    |____ webpack-stats.json # important file number 2
|
|
|____ my_project/             # root backend
|    |____ __init__.py
|    |____ settings.py
|    |____ urls.py
|    |____ views.py
|
|
|____ templates/                
|    |____ index.html         # django template that houses vue
|
|____ .gitignore
|____ venv/
|____ requirements.txt
|____ manage.py
|____ db.sqlite3
Enter fullscreen mode Exit fullscreen mode

We'll start with the backend. Create an empty directory for your project, and then run:

$ django-admin startproject my_project && cd my_project
$ mkdir venv && python3 -m venv venv && source venv/bin/activate
$ (venv) pip install django djangorestframework
$ (venv) mkdir templates && cd templates && touch index.html
$ (venv) cd ..
$ (venv) mkdir core && touch __init__.py && touch views.py
Enter fullscreen mode Exit fullscreen mode

This is all we need to do with Django for now. We'll come back to the templates/index.html and core/views.py files later. This next command assumes you've already installed vue globally. If you haven't, simply run: npm install -g vue-cli

$ vue create frontend
Enter fullscreen mode Exit fullscreen mode

Then walk through the setup wizard so your presets look something like this (or customize your presets as you prefer):

$ Vue CLI v4.3.1
  ? Please pick a preset: Manually select features
  ? Check the features needed for your project: Babel, Router, Vuex
  ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
  ? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
  ? Save this as a preset for future projects? (y/N) N

  ### installation magic happens...

🎉  Successfully created project frontend.
👉  Get started with the following commands:
$ cd frontend
$ npm run serve
Enter fullscreen mode Exit fullscreen mode

Now we have separate frontend and backend environments! The frontend server will be at http://localhost:8080/ which will display the Vue landing page, and the backend will be at http://127.0.0.1:8000/ which will display the Django landing page. You can kick off the frontend per above commands and kick off the backend in the root directory with:

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Now, these environments just need to be able to talk to each other. The two tools we will use to accomplish this are webpack-bundle-tracker on the Vue side and Django webpack loader on the backend.

$ cd frontend
$ npm install webpack-bundle-tracker@0.4.3
Enter fullscreen mode Exit fullscreen mode

We need version 0.4.3 of webpack-bundle-tracker because the files Django is expecting will not be automatically generated in the same way if we use the latest alpha version. Please note, 0.4.3 is the latest stable release as of April 2020. If we don't specify this version, npm will pull down the latest alpha version which will not work the same. Next we need to create the vue.config.js file in the frontend directory:

$ touch vue.config.js
Enter fullscreen mode Exit fullscreen mode

and fill it with these contents:

const BundleTracker = require("webpack-bundle-tracker");

module.exports = {
  // on Windows you might want to set publicPath: "http://127.0.0.1:8080/"
  publicPath: "http://0.0.0.0:8080/",
  outputDir: "./dist/",

  chainWebpack: (config) => {
    config
      .plugin("BundleTracker")
      .use(BundleTracker, [{ filename: "./webpack-stats.json" }]);

    config.output.filename("bundle.js");

    config.optimization.splitChunks(false);

    config.resolve.alias.set("__STATIC__", "static");

    config.devServer
      // the first 3 lines of the following code have been added to the configuration
      .public("http://127.0.0.1:8080")
      .host("127.0.0.1")
      .port(8080)
      .hotOnly(true)
      .watchOptions({ poll: 1000 })
      .https(false)
      .disableHostCheck(true)
      .headers({ "Access-Control-Allow-Origin": ["*"] });
  }

  // uncomment before executing 'npm run build'
  // css: {
  //     extract: {
  //       filename: 'bundle.css',
  //       chunkFilename: 'bundle.css',
  //     },
  // }
};
Enter fullscreen mode Exit fullscreen mode

Comment out the base url settings that comes with Vue's router. If you skip this, you'll just end up having a path like this: http://127.0.0.1:8000/http:/0.0.0.0:8080/blah-blah-blah
to which I say - ew wtf?
Fix it by removing the base url config:

const router = new VueRouter({
  mode: "history",
  // base: process.env.BASE_URL,
  routes
});
Enter fullscreen mode Exit fullscreen mode

Once you create this file, spin up the frontend development server, and a webpack-stats.json file will be generated

npm run serve
Enter fullscreen mode Exit fullscreen mode

Now navigate to the root directory and ensure your virtual environment is activated so we can install django-webpack-loader. Also, feel free to generate your requirements file:

$ (venv) pip install django-webpack-loader
$ pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

Then, add webpack_loader to Django's installed apps in settings.py, and add the following changes to the settings.py file:

INSTALLED_APPS = [
         ...
    'rest_framework',
    'webpack_loader',
]

    .
    .
    .
TEMPLATES = [
    {               ...

        'DIRS': [os.path.join(BASE_DIR, 'templates')],
                    ...
    },
]
    .
    .
    .

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "assets"),
    os.path.join(BASE_DIR, "frontend/dist"),
]

WEBPACK_LOADER = {
    'DEFAULT': {
        'BUNDLE_DIR_NAME': 'dist/',
        'STATS_FILE': os.path.join(BASE_DIR, 'frontend', 'webpack-stats.json')
    }
}

Enter fullscreen mode Exit fullscreen mode

Paste this into your templates/index.html file:

{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- This part is in the screenshot at the bottom! -->
    <h1>Vue JS</h1>
    <div id="app"></div>
    {% render_bundle 'app' %}
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Paste this into your core/views.py file:

from django.conf import settings
from django.views.generic.base import TemplateView


class IndexTemplateView(TemplateView):
    def get_template_names(self):
        template_name = "index.html"
        return template_name
Enter fullscreen mode Exit fullscreen mode

Make sure these are reflected in your my_project/urls.py file:

from django.urls import re_path
from core.views import IndexTemplateView

urlpatterns = [
    ...
    re_path(r"^.*$", IndexTemplateView.as_view(), name="entry-point"),
]

Enter fullscreen mode Exit fullscreen mode

Once these changes are made, spin up your npm server in one terminal tab, and then spin up your Django dev server in another terminal tab and you should be good to go! Happy coding 😎

$ npm run serve
Enter fullscreen mode Exit fullscreen mode
$ (venv) python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

hooked up screenshot

Top comments (8)

Collapse
 
faziki profile image
Pieter Uys • Edited

Hi Jordan,

My setup works (kinda) The Django side is using the index.html file within the templates folder created in this tutorial but the Frontend is using the default Vue index.html in the public folder

I don't think this is intended to be so if it is then I'm probably looking for something different.

Looking for a way to make Django do the middle/backend stuff and Vue to ONLY handle to front end stuff. If you know of a way please let me know.

Edit* I found what I needed on GitHub but it was outdated so I updated the libraries to the latest. There is some problems that I found but I will fix them as time goes by

Anyone that needs a template with the following

  • Vuejs
  • Vuex
  • Router
  • PWA
  • Django 2.2.15
  • Django Rest API

here is the repo github.com/Faziki/-django-vuetify-...

Sorry Jordan for jacking your post :(

Collapse
 
marshallgeorge44 profile image
marshallgeorge44

Worked perfectly thanks, any recommendation on other tutorials that explain how to deploy this setup to a production environment?

Collapse
 
divinealien profile image
Alien

cool

Collapse
 
mailthat profile image
mejl • Edited

Didn't work, I only get the code from localhost 8000
I run both 8000 and 8080

Collapse
 
pabliyocr profile image
PabliyoCR

How can I get Production files?

Collapse
 
8area8 profile image
Mikael Briolet • Edited

It deserves more explanations, but it remains one of the best tutorials on the subject.

Collapse
 
jeanluis019 profile image
Jean Luis Lopez

After implementing all this, what is the recommend way to make deployment to production?

Collapse
 
xtrmcoin profile image
xtrm-co-in

Did you get a workaround for deploying to production yet ? I am also looking for the same.