Django and Vue.js: Let's have fun together

ยท 1010 words ยท 5 minute read

I love Python. Every time I write code I prefer it to be in Python. So, naturally, when I write a web service my #1 tool of choice is Django. This was also true as my girlfriend asked me a while ago if I couldn’t create as simple meal planner for her. Nothing fancy, just a simple view where we could organize our meals.

In times of stay-at-home orders it was rather easy to find time for projects like this. So I quickly implemented a few data models and views and quickly had a rather static version of the meal planner. However, each time I add a meal the whole page would reload. This is annoying when you plan meals for a whole week for two persons.

I had tinkered earlier with Vue.js for smaller projects but it never worked out because the separation of frontend and backend caused more problems for me than I got benefits from this approach. But after stumbling across a blog post from Thomas Kainrad that demonstrated the basics of the integration between Django and Vue.js, the goal of using Django and Vue.js together felt achievable. Add to this another awesome post by Pascal Widdershoven about configuring Webpack to use in Django and nearly all the pieces fell into place.

Preparing Django for Vue.js ๐Ÿ”—

Create TemplateView ๐Ÿ”—

First of all, you need a view inside Django that will include your Vue.js application. You can use a TemplateView for this or any other view. For my meal-planning app Iโ€™m using a DetailView. The template could look like this:

{% extends 'base.html' %}
{% load static %}

{% block content %}
  <div id="app">
  </div>
{% endblock %}

{% block footer_scripts %}
  <script type="text/javascript" src="{% static 'frontend/js/chunk-vendors.js' %}"></script>
  <script type="text/javascript" src="{% static 'frontend/js/app.js' %}"></script>
{% endblock %}

In the content block we create an empty HTML tag with the id of the Vue.js application; typically app. The other thing we do is to load the generated Javascript files. I put them in a subfolder of the static directory (here called frontent). This way I can easily gitignore files that shouldn’t be version-controlled but still keep other files in static/ untouched.

Set up Django REST Framework ๐Ÿ”—

In a real-world scenario having a TemplateView is typically not enough, but you also need an interface to fetch and create data. My tool of choice for this is Django REST Framework. Refer to their quickstart guide if you’re not familiar with the framework.

Configure Django ๐Ÿ”—

The last thing you have to do in you Django setup is making sure that the static content is served correclty. Therefore, set the STATIC_URL and STATICFILES_DIRS in your settings.py:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "mymodule/static"),
]

Create a Vue.js project ๐Ÿ”—

There are several ways to create a new Vue.js application. The easiest is to integrate your whole Javascript code in the Django template file itself. But when you want to utilize the power of components and need to manage a few dependencies this approach becomes unwieldy pretty quick. We are going to set it up in the recommended way via vue-cli. First install the CLI:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

Then create a new project:

vue create myproject

Now you have a working HelloWorld Vue.js app in place. I created the Vue.js project inside my Django project. That way it’s easy to reference the file names and I don’t have to handle to Git repositories.

Run yarn serve to see your Vue.js app in action.

Configure Vue.js to work with Django ๐Ÿ”—

Up until here everything was straightforward and more or less like you see in every Django or Vue.js tutorial. But now comes the important part: configuring Vue.js to work seemlessly inside a Django app. If you know what you need to do it’s pretty easy. If you don’t - like me before the meal-planning app - it’s a confusing endeavor.

After reading Pascal’s article about configuring Webpack, I knew the obvious first step was to configure the output path correctly, so that Django can pick the generated files up. However, there is no webpack.config.js in the newest version of Vue.js; the central place for configuration is vue.config.js. This file does not exist in a new project so you have to create it manually. But this file does not only take care of the Vue.js config itself, but also wraps the Webpack configuration.

To set the correct output path in Webpack without Vue, the configuration looks like this (slightly abbreviated from Pascal’s post):

output: {
  path: path.resolve(__dirname, "mymodule/static"), // Should be in STATICFILES_DIRS
  publicPath: "/static/", // Should match Django STATIC_URL
},
devServer: {
  writeToDisk: true, // Write files to disk in dev mode, so Django can serve the assets
},

To set the same values in the vue.config.js, use these values:

const path = require("path");

module.exports = {
  outputDir: path.resolve(__dirname, "../mymodule/static/frontend"),
  publicPath: "/static/frontend/",
  devServer: {
    writeToDisk: true
  }
}

Here, mymodule is the name of the Django app your Vue project belongs to. You can also use the global static directory of your project if you prefer. Vue.js now writes all the files in the correct directory and your Django template should be able to pick up the generated Javascript files:

  <script type="text/javascript" src="{% static 'frontend/js/chunk-vendors.js' %}"></script>
  <script type="text/javascript" src="{% static 'frontend/js/app.js' %}"></script>

However, Vue.js generates an index.html file as its entry point. This is unnecessary since we already have a HTML file in place. This is the final piece in our configuration. To suppress this behavior extend your Vue config as follows:

// disable hashes in filenames
  filenameHashing: false,
  // delete HTML related webpack plugins
  chainWebpack: config => {
    config.plugins.delete('html')
    config.plugins.delete('preload')
    config.plugins.delete('prefetch')
  }

Guess what? That’s it! ๐ŸŽ‰ Your Vue.js application is now fully integrated in your Django project and you don’t have to worry about things like authentication when communicating with your Django backend and you have all benefits in Vue.js like package management and hot-reloading .

Are you interested in a more detailed explanation or a sample project? Let me know in the comments or on Twitter.