Artificial Intelligence

Instant reload with Django, NPM, and Browsersync

Here at Metal Toad, we are starting to use NPM as a task runner to automate our development processes.


Here at Metal Toad, we are starting to use NPM as a task runner to automate our development processes. This tutorial shows how to automate CSS compilation and instantly reload the browser when files change.

Objectives

In this post, we will be setting up an automated local build environment with the following goals in mind:

  • A single command to start the dev server and start watching for file changes
  • Automatic compilation of Sass stylesheets into CSS
  • Automatic reload of the CSS without reloading the full page in the browser
  • Automatic reload of the browser tab when a Python, JavaScript, or HTML file changes
  • The ability to run all tasks using NPM, without needing another task runner like Grunt or Gulp

Setup

Before we can set up the build scripts, we will need the following:

  • Python and Pip installed
  • A Django site. The Polls site from the Django Tutorial will work fine.
  • Node and NPM installed

Configuring the NPM task runner

Creating your package.json

Change directory into the root of your Django project. This should be the same folder where manage.py is located. Then, initialize your NPM project and install some packages:

npm init
npm install node-sass concurrently browser-sync --save-dev

What these packages do

node-sass is the Sass compiler for NPM.

concurrently improves NPM's handling of parallel task execution.

browser-sync is a lightweight development server which provides live-reload capability. We will use it in tandem with the Django development server.

Configuring the NPM scripts

Now that we have our packages installed, there will be a package.json file in our project folder. Edit it in your favorite text editor and add the following:

{
  ...
  "scripts": {
    "css-compile": "node-sass myapp/static/scss -o myapp/static/css",
    "css-watch": "node-sass myapp/static/scss -o myapp/static/css --watch",
    "browser-sync": "browser-sync start --files \"myapp/static/css/*.css, myapp/static/js/*.js, myapp/**/*.py, myapp/templates/*.html\" --proxy 127.0.0.1:8000 --reload-delay=300 --reload-debounce=500",
    "start": "concurrently --kill-others \"npm run css-watch\" \"python manage.py runserver\" \"npm run browser-sync\" "
  },
  ...
}

You will need to adjust the file paths in the 'css-compile', 'css-watch', and 'browser-sync' lines to match your project.

Running your new environment

This package.json file includes several separate NPM tasks like 'css-watch' and 'browser-sync'. The concurrently package lets us wrap all these commands, along with the Django development server, into one command to rule them all, `npm start`.

If you are using Python's virtualenv, you will need to activate your virtual environment before running `npm start`.

Type `npm start` into your command prompt. This will start watching your project files for changes, and a browser window will appear with the front page of your site. Whenever you edit a Python, JavaScript, or HTML file, the page will reload automatically. When you edit an SCSS file, your stylesheets will be compiled into CSS, and the CSS in the browser will reload, without having to reload the entire page.

Browsersync and the Django development server

Browsersync injects a JavaScript snippet into the body tag of the pages it serves, which sets up the live reload. It can't serve a Python application, however. The good news is that it has a built-in proxy, which we're using to relay requests to the Django development server.

Here, we have given Browsersync a few command-line options:

  • --files \"myapp/static/css/*.css, myapp/**/*.py, myapp/templates/*.html\": This option tells Browsersync to watch all CSS, Python, and HTML files in the specific locations listed. You will probably need to update these paths, or add new ones, to match your project.
  • --proxy 127.0.0.1:8000: This tells Browsersync to proxy its requests to the Django development server
  • --reload-delay=300: This makes Browsersync wait for 300 milliseconds after it detects a file change before reloading the page. This prevents race conditions between Browsersync and Python. Without this, you may see pages fail to load when you edit a Python file. If your browser starts to reload and the loading spinner just spins forever, increase this value, then stop and start the npm task.
  • --reload-debounce=500: This prevents the page from reloading more than twice a second, in case you have fast fingers.

Troubleshooting

The server runs, but nothing happens when I edit a file

Check the paths to the files in your package.json and make sure they match the locations of the files in your projects. You may need to add or remove paths.

"Unable to determine the domain name" error with Browsersync

If Browsersync fails with an error "Unable to determine the domain name", you need to inspect the command arguments. Use '127.0.0.1' instead of 'localhost', don't include the protocol, and don't enclose the address in quotes.

  • Bad: --proxy localhost:8000 (this should work but doesn't, possibly due to this bug)
  • Bad: --proxy '127.0.0.1:8000'
  • Bad: --proxy https://127.0.0.1:8000
  • Good: --proxy 127.0.0.1:8000

NPM command errors

This setup is designed to use the project versions of the node packages, rather than the global versions. On some systems, you may see errors that the 'node-sass' or 'browser-sync' commands are not found. If this happens, add the following to your package.json:

{
  ...
  "bin": {
    "node-sass": "/.bin/node-sass"
    "browser-sync": "/.bin/browser-sync"
  },
  ...
}

What about Apache and mod_wsgi?

If you are hosting your local development site using Apache and mod_wsgi, you should be able to use most of the techniques shown in this post. You would need to remove the "python manage.py runserver" command from the 'npm start' line in package.json, and change the proxy address provided to Browsersync. Your package.json file might look something like this, assuming 'myapp.dev' is the address of your local development site:

{
  ...
  "scripts": {
    ...
    "browser-sync": "browser-sync start --files \"myapp/static/css/*.css, myapp/**/*.py, myapp/templates/*.html\" --proxy myapp.dev --reload-delay=300 --reload-debounce=500",
    "start": "concurrently --kill-others \"npm run css-watch\" \"npm run browser-sync\" "
  },
  ...
}

Next steps

We could easily add JavaScript minification, CSS minification, image processing, etc. by adding additional commands to `npm start`.

Compatibility

This setup has been tested with Django 1.9 and NPM 3.x on Windows, Ubuntu/Mint, and OSX.

Conclusion

The ideas shown here should also work with platforms other than Django. You would only need to adjust file paths and replace the Django dev server command with a different command.

Happy coding!

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.