Getting Started with Webpack and Django
This walkthrough is aimed at providing a starter template for weaving Webpack into your front end Django workflow. Our goal is to create a project template that allows us to keep all of our source under version control, process our front end code locally with Webpack, and push everything out to production from Git.
Fire up a cup of tea, get ready for some command line use, and let's run through what we're going to do.
Or just skip all of this and grab my project template from GitHub.
django-webpack-scaffolding.
Set Up Our Development Environment
- Create a Virtualenv
- Install Django
- Run Startproject From My Template
- Install Django Dependencies
- Update Virtualenv's bin/activate
- Install Node Dependencies Locally
- Build Webpack Bundles Locally
- Run Webpack Dev Server Locally
- Run the Django Dev Server
- Verify in the Browser
- Workflow Recap
- Build Bundles for Production
Go Over Our Production Workflow
Next Steps
For additional details on using Django with Webpack and for further integration with ReactJS see Owais Lone's excellent article.
Requirements: Some understanding of Django, Python, Node, and Git. See this article for an overview of Git if you haven't used it.
Getting Started
Webpack's advantages are many. Chiefly it will process Sass (I've included Sass loaders, but you can also use Less.) Additionally, we can start using ES6 Javascript syntax and framework goodies like ReactJS. When we're ready, Webpack will wrap everything up into a beautifully uglified package.
Technically, we don't need Node on our production server since we're building locally and following Django's normal collectstatic process.
But for your dev setup, make sure you have Node, NPM, and Virtualenv installed.
node -v
should yield > v4.2.2
npm -v
should yield > 2.14.7
virtualenv --version
should yeild > 13.1.0
Node installation
Virtualenv installation (OSX)
Create a New Virtualenv
You know those clear plastic boxes with lots of little compartments for organizing nuts and bolts and screws? That's what a Virtualenv does. They keep our Python dependencies together in one place.
Open a new Terminal window and create a new virtualenv. I like to create them in a master projects folder called: ~/virtualenvs
. Feel free to change this and do whatever you want, and feel free to replace projectname with the name of your project.
For Django 1.9 we're going to use Python 3.
virtualenv -p python3 projectname && cd projectname
This should give you a folder structure like this:
~/virtualenvs/projectname
├── bin
├── include
└── lib
And then we need to activate the virtualenv using the command:
source bin/activate
Install Django
With an active virtualenv we can now install Django. (We need to install Django first in order to use the django-admin command.)
pip install django==1.9.6
Start from the Template
Now we can build our project from the template. The startproject command accepts a parameter called --template that will grab the project from Git. By all means, please replace projectname in the command with the name of your project. ( We're still in the same virtualenv folder .)
django-admin startproject projectname --template=https://github.com/toymakerlabs/django-webpack-scaffolding/archive/master.zip --extension=js,json
>Troubleshooting Tip If you have many virtualenvs, sometimes DJANGO_SETTINGS_MODULE can stick from another project.
...
File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
File "<frozen importlib._bootstrap>", line 2224, in _find_and_load_unlocked
ImportError: No module named 'xxyyzz'
If Django throws an error like the above, try echo $DJANGO_SETTINGS_MODULE
. If it is not a blank space, run deactivate
and make sure DJANGO_SETTINGS_MODULE is not set in bin/activate or set it to a blank space ""
(see here)
When installed, our virtualenv directory should look like this:
~/virtualenvs/projectname/
├── bin
├── include
├── lib
├── pip-selfcheck.json
└── projectname
Install Django Dependencies
Now we need to install Django dependencies. (In this case, django-webpack-loader.)
cd projectname
pip install -r requirements.txt
Once running, Django Webpack Loader will look for the presence of a webpack-stats.json file, which will tell Django where to find our static files depending on the environment we're in.
Update Virtualenv's 'bin/activate' Script
I like to use a split-settings configuration in my Django projects. Splitting the settings file allows us to change the environment we're working in from within the virtualenv. This saves us from needing two separate settings files that are outside of version control.
Open ../bin/activate in your editor of choice and paste the following at the bottom of your file (change projectname to the name of your project)
DJANGO_SETTINGS_MODULE="projectname.config.settings_development"
export DJANGO_SETTINGS_MODULE
Then activate the environment again to apply the settings change.
source ../bin/activate
>Tip:
Verify the value of DJANGO_SETTINGS_MODULE by echoing it in the terminal: echo $DJANGO_SETTINGS_MODULE
. It should print: projectname.config.settings_development
Install Node Dependencies
Now we need to install Webpack and Webpack's supporting modules. My Django project template includes a package.json file that will allow us to install everything through npm.
npm install
(Npm will warn you that you need to update package.json with your description, version, and repo. Go ahead and do that at your leisure.)
If everything installed correctly, you should have a wall of text in the terminal but no big red error-looking stuff.
>Note: We can skip this step for production since we're building our production JS locally and committing it to source.
Nice We're getting there. Now is a good time for a refill.
Build an Initial Bundle
Package.json contains some npm shortcut commands that assist in building our asset bundles and running the dev server. Let's test our Webpack config by making an initial build.
npm run build
This command creates a folder called /static_src/bundles which is like the staging area for Webpack. Static assets are served locally by Webpack Dev Server from this directory.
The npm run build
command will also create webpack-stats.json, which will tell Django how to assemble the URLs of our static assets.
The basic workflow is to run npm run build
when you're in a good spot and ready to commit code changes.
Start Webpack
Let's kick the tires and fire up Webpack Dev Server. The dev server will monitor our files for changes and continuously build automatically.
npm run watch
The terminal should output a few messages, the first should let you know that the dev server is running on 0.0.0.0:3000 If everything worked, it should look like this:
Listening at 0.0.0.0:3000
Hash: 58222d8f24ef0415d367
Version: webpack 1.13.0
...
webpack: bundle is now VALID.
Valid bundle! Yas!
Run the Django Development Server
We need to leave Webpack Dev Server running in order for it to serve our static files. So open up a new terminal window and activate the environment (The new window should open to the same projectname directory. If not, cd there.)
source ../bin/activate
We might as well create an initial migration first, since Django will give us a warning.
python manage.py migrate
Now let's run the dev server!
python manage.py runserver
We should get this output if it's working:
Django version 1.9.6, using settings 'projectname.config.settings_development'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Check it in the browser
Open your browser and paste:http://127.0.0.1:8000/
POW! Congrats! You're rocking with Webpack and Django!
This is a basic index view, but we can see that Webpack Dev Server is serving our static assets. One thing to remember: Webpack Dev Server is serving the assets from memory, so fear not if you don't see actual changes to the files in /static_src/js/ and /static_src/css/
When we run npm run build
it will write our changes to our static files to /static_src/bundles/ The bundles folder also contains a map file, which is useful for debugging.
Development Workflow Overview Recap
- Open an new terminal and run
npm run watch
. - Open another window and start the Django dev server.
- Develop. Develop. Develop.
- When you're ready to commit, pause webpack dev server and run
npm run build
- Profit
Create a Production Bundle
Since part of our workflow involves committing everything to source, we need to create a production bundle and commit webpack-stats-production.json before we push our project.
Head back over to the terminal running Node and enter ^c
to cancel the watching process.
Now we can run:
npm run build-production
This will create a new folder under /static/dist/ with our minified production bundles, and our webpack-stats-production.json file that will tell our production Django how to grab our static files.
* Optional: Test the Production Bundle Locally
If you're keen to give it ago, we can test the production bundle locally by making a few changes to projectname/config/settings_development.py
First set debug to False and ALLOWED_HOSTS to ***
########## DEBUG CONFIGURATION
DEBUG = False
ALLOWED_HOSTS = ['*']
########## END DEBUG CONFIGURATION
And then collect static. The collectstatic commmand will grab all of our static assets and copy them to the STATIC_ROOT defined in our settings.
python manage.py collectstatic
Now lets run the dev server again, only with the --insecure flag. The insecure flag tells django to to continue to serve assets with the dev server, even with debug=False. (Please dont do this on production >.<)
python manage.py runserver --insecure
Open the browser and head to http://127.0.0.1:8000.
If we look in the browser console, we should see that our static assets are serving from /static/dist/, and not from http://localhost:3000/
Now let's Flip debug back on in settings_development before we forget!
DEBUG = True
ALLOWED_HOSTS = []
And you may ask yourself, how did I get here?
Yes, that's from the Talking Heads. Let's take a second to go through our virtualenv folder and see what we've created.
~/virtualenvs/projectname/
├── projectname
//Webpack config and Django settings are stored here under /config/
├── manage.py
├── node_modules
├── package.json
├── server.js
//Starts our Webpack Dev Server using the command: npm run watch
├── static
│ ├── dist
│ └── js
//Static is in our STATICFILES_DIRS setting. We can still use Django's method of including static files, but Webpack won't compress the files in here. Handy for legacy code or shims.
├── static_src
│ ├── bundles
│ ├── css
│ └── js
//This is where we develop our front end code. Webpack will bundle up everything in here through use of import or require (es6 vs es5.) /static_src/ is not in STATICFILES_DIRS because I thought it's good not to have the unminified bundles collected to production static root.
├── staticfiles
│ ├── admin
│ ├── dist
│ └── js
//Staticfiles is our static root. This is just a temporary location for the example. Ideally this would be in your web root of Nginx or Apache, like: /var/www/static/,
├── templates
│ ├── base.html
│ └── index.html
├── webpack-stats-prod.json
└── webpack-stats.json
// Webpack stats files are used by the django-webpack-loader templatetag to choose which static files to render depending on the environment
Production Workflow
Now that we have everything we need, it's a good time to initialize a new project repository from the projectname directory, commit, and push it.
Recommend Production Stack
For production environments, I recommend Postgres, Gunicorn, and Nginx. Our friends at Digital Ocean have a fantastic guide for getting a production environment up and running. See: How to set up Django with Postgres, Nginx, and Gunicorn
If you follow the article, skip the step: "Create the Django Project" and instead, clone the project we created, and adjust projectname/config/settings-production.py
However there are many ways of running Django in production. Your production setup will likely differ from mine, so let's talk workflow.
Production Workflow Overview
Let's say we've squashed a bug in the front end. Before we deploy to production, we need to tell Webpack to bundle and compress our static assets.
npm run build-production
This command sends our compressed assets to our static distribution folders in
static/dist/js/ and static/dist/css. It will also update webpack-stats-production.json which Django Webpack Loader uses to determine which static files to use based on our Webpack config.
When our build passes, we're ready.
- Run
npm run build-production
to create production static bundles. - Commit everything to your repo
- Push to your repository
- Log in to production
- Pull from Git
- Run
python manage.py collectstatic
for static file changes to take effect.
I put up my test example in Git here Feel free to take a peek!
Where To Go From Here
Now that we have a starter project template, we can start adding cool things like Django Rest Framework and ReactJS.
Django Rest Framework
ReactJS
Django and ReactJS
Thanks for following along! Enjoy working with Django and Webpack.