UPDATE: I recently created a working project at ariera/django-vue-template using the latest version of the vue-webpack template and bundling all the changes highlighted in this post in just this commit 8a5c552. You may also be interested in checking a vue project template that CharlesAracil created taking inspiration from this entry.
I recently started a new web project based on both Django and Vuejs. It took a good deal of reading half-related and/or outdated posts, tons of documentation, and a good deal of trial and error to get it right.
In this post we will cover the key points on setting up a nice and solid development environment that is consisten with production and hence easy to deploy. The specifics of how to deploy, final configuration touches and (automation) will be discussed in a future article.
This is the solution that I found, there may be better alternatives (which I’d be happy to read) and it is utimately written with the intention of helping others as well as my future-self :)
Both Django and Vue offer local web servers to facilitate development, but that confused me in the beginning. Does that mean that I need to be running both servers in parallel? And do I need to switch from one to another while developing? That sounds cumbersome! If you are building a full SPA with Vue as your frontend, and Django as a simple API backend that might be okey, but I want something else.
I want to remain flexible: some parts of my project will behave like an SPA, other parts will be rendered by Django, and some other will be a mixture: Django-rendered with Vue components. I want to be able to use my webpack-compiled css in my Django templates, as well as other static assets.
- minimise differences in between prod & dev
- use just one url in development (ie Django’s runserver should proxy to webpack’s dev server, or the other way around)
- preserve hot reload (ie. HMR) in development
- django backed pages should work without much ceremony
The solution I found is based around the django library
django-webpack-loader and the
webpack-bundle-tracker webpack plugin. The django module will read a manifest file, and a webpack plugin take care of updating it. That way, webpack will compile all the assets and Django will be able to use them in your templates.
All you will need to do while developing is launch both local web servers and point your browser to the Django one :)
Versions i’m using:
- Vue.js 2.4.2
- Django 1.11
- vue-cli 2.8.2
- Python 3.6.1
Setting up the dev environment
Creating the project
I have used the vue-cli to create a new project based on the webpack template, it will do most of the configuration and setup for us. Then, inside the new directory holding my project, I had created a new django project:
Now install both libraries
django-webpack-loader to your
INSTALLED_APPS, we will get to the specific configuration in a minute.
And configure webpack to use
Defining the statics paths
This part was one of the trickiest, there are several configuration options all with very similar names and they feel very confusing to new commers to the framework. I’ll spend a little explaining what and why every option does.
My goal here was to have in the end a directory named
js dir would be okey, but theres no need for deeper levels). The final
./public folder would look like this
- public/ - js/ (webpack generated) - app.c611f8fd9b27da4ec95f.js - app.c611f8fd9b27da4ec95f.js.map - ... - css/ (webpack generated) - ... - img/ (django generated) - ... - admin/ (django generated) - ...
In order for this to happen we have to configure both worlds properly. Let’s take a look at the final configuration of both Django and webpack, and then we will discuss them.
Let’s analyse this step by step.
build.assetsRoot: path.resolve(__dirname, '../dist/')
This is the output directory of our
npm run buildtask, that takes care of compiling and building all of our webpack-controlled assets. We will run this task as part of the deployment process. It will take care of analysing all our webpack entry points, traversing and compiling them. The out put will be stored in the
./distdirectory at the root of our project (which should be gitignored).
When deploying to production we will also run the Django script
collectstaticthat takes care of finding all the static files our project makes use of, and putting them in the correct folder. With
STATICFILES_DIRSwe are telling it were to look for these files. With
STATIC_ROOTwe specify where to place them.
STATIC_DIRSwe have specified 2 directories: one is the
./static(Django’s default), and the above defined
./distwhere webpack will place its compiled assets.
Now we tell webpack that (in production) any asset reference should point to
/static(ie at the root of the domain). For example, if our code reads:
the final compiled version will be:
STATIC_URL = '/static/'
The same thing but for Django. Everytime one of our templates referes to a static file it will render it under the
This is one of the key points in which we achieve our goal number 2 (use only one url in dev). We want the webpack dev server to serve our webpack assets. While we point our browser to Django’s
localhost:8000, webpack will compile our code to point at
localhost:8080(notice those are 2 different ports).
WEBPACK_LOADER['DEFAULT']['STATS_FILE']we point to the json manifest file generated by webpack’s
BundleTracker(see above in file
And we set to blank
WEBPACK_LOADER['DEFAULT']['BUNDLE_DIR_NAME']because we don’t want to nest our webpack assets into any subdirectory.
Referencing static files from both Vue and Django
One final touch. I would like to have a place to put images, fonts, or generic statics assets that can be used from both worlds: Django tempaltes and Vue components. In other words, I want to have a
./static directory at the root of the project where I could put (for example) my logo and be able to write this code:
The Django example already works thanks to the config above described (see
STATICFILES_DIRS), but for Vue/Webpack to work you’d need to add an alias to the wbepack config, like this.
Thanks to this we can now write our Vue code like this (mind the
Hot realod or Hot Module Replacement (HMR)
While in development we are only accessing Django’s server, and proxying our asset requests to the webpack server (see above
dev.assetsPublicPath). Because of this HMR or hot reload is broken. We need to configure webpack’s hot middleware to point to the right path, and we need to add a new header to the webpack dev server so it allows for CORS requests.
The official vue webpack template was updated around 2 weeks ago to start using the official
In order to properly configure HMR after the update you simply need to add the following to the
Thank you very much to Emilien and LinusBorg for the feedback on this :)
The basic Django template
We will define a Django template reusable by other parts of our application that will take care of including the webpack assets.
Notice that in order for this template to be detected by Django you need to add the following to your
And in our
main.js where we define the root component of our SPA, we will point to this
The last step would be to define a new url route in our Django settings:
And we can now open
http://localhost:8000 in our browser and enjoy our fully functional dev environment :)
Clean unnecessary files and options
UPDATE: It is better ignore everything related to
HtmlWebpackPlugin in this part, it was breakign the end-to-end tests.
Before we conclude let’s do a little cleanup. Remove the
index.html file at the root of your project and stop using
HtmlWebpackPlugin in development. Since Django will render the initial html and include any assets there we don’t need
HtmlWebpackPlugin to generate an initial html file for us.
For the same reason we want to remove it from the production config, but we want to keep it in tests. Simply move this bit of conde from
And last but not least lets add the following to your
To have a good dev environment we have used Django as our main dev server, which will in turn proxy any asset request to webpack. This required a good deal of configuration. In doing so we have simplified the development workflow so we can spend more time programming and less time switching between browsers or fine tuning the config for new cases that may appear.
We have also paved the path to production deployment, but that’s something that we will cover in the next post.