How to Serve an Angular Single Page Application using Django – Part 3

Here’s part 1 of my series of how to serve an Angular 6 SPA web application in Django, without modifying the Angular CLI generated html. 

Putting it all in a Docker Container

I use Docker for development projects as it gives me a clean development environment where I can have all my dependancies isolated from other projects and stuff running on my system. 

Disclaimer: this may not be the best way to do Django and Angular in Docker. I chose this method because I wanted to have my Angular app served by Django to avoid CORS problems and just keep the architecture as close to what it’ll be in production as possible. Obviously it wouldn’t be running on Django’s runserver in production however. 

Here’s how to build a docker configuration to run my Django frontend and Angular backend together in the one container. 

First create a docker-compose.yml file:

version: '3'

services:
  db:
    image: postgres
  web:
    build:
      context: .
      dockerfile: Dockerfile
    working_dir: /code
    env_file:
      - web_variables.env
    command: sh devservers.sh
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

This docker-compose file first creates a postgresql database container and then a web container using a Dockerfile. When the container is started it runs the devservers.sh script we created in Part 2.

It mounts the current directory(the django root) to /code within the container. It then exposes port 8000 inside the container to port 8000 outside the container. 

In the Dockerfile put:

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash -
RUN apt install nodejs
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
RUN mkdir -p /code/static
WORKDIR /code/frontend
RUN npm install -g @angular/cli
RUN npm install
RUN ng build --outputPath=/code/static

This creates a new Docker container from the Python 3 official image. It creates a /code directory in the container.

It then installs node.js as as dependancy for the Angular CLI and installs all the Django project dependancies from the requirements.txt file. 

Finally to test that the environment is ready it copies in the code from the Django root, installs the Angular CLI globally, installs the Angular project dependancies and does a test build of them.

Note that in the docker-compose, we’re telling it to mount the current working directory as a volume on /code. So the /code path in the container will be replaced with the Django root from the host system. 

Really this means that the lines from ADD onwards in the Dockerfile are unnecessary. However I’ve left them in there to just ensure that that we know about it at image build time if the environment won’t be ready to use. 

Using the Docker environment

To start the dev servers run the following command in the Django project root:

docker-compose up -d

As your Django project root is mounted in the Docker container, any changes you make to the Django/Angular project will also be in /code in the Docker container. The Django manage.py runserver will automatically detect any changes on the Django side and recompile and serve them.

Angular build watch will detect changes to the Angular code in <Django project root>/frontend and rebuild them, putting the output SPA into /code/static where it’s served by Django’s static file serving. 

If you add new files or npm dependancies to the Angular project, you’ll need to restart the web container using:

docker-compose restart web

If you add new dependancies to the Django project do a full rebuild with:

docker-compose up --build -d 

In my case I actually run my dev install on a different machine than the one I write code on, so I just upload via Webstorm or PyCharm to the project root on my dev server. The changes are detected and ready to use within seconds.