wiki.rogs.me

my personal wiki

User Tools

Site Tools


Sidebar

backend:drf-starter

Django REST Framework starter

I mainly use Django REST Framework for backend development. For a starter, I use the basic django-admin.py startproject myproject inside of a docker container with PostgreSQL. Here is local my docker configuration:

Dockerfile

The Dockerfile includes all the packages to compile Python and work with pictures (PIL).

FROM python:3.7-alpine
MAINTAINER rogs
 
ENV PYTHONUNBUFFERED 1
 
COPY ./requirements.txt /requirements.txt
RUN apk add --update --no-cache postgresql-client python3-dev \
  libffi-dev jpeg-dev freetype-dev libjpeg-turbo-dev libpng-dev
RUN apk add --update --no-cache --virtual .tmp-build-deps \
  gcc libc-dev linux-headers postgresql-dev musl-dev zlib zlib-dev
RUN pip install -r /requirements.txt
RUN apk del .tmp-build-deps
 
RUN mkdir /app
WORKDIR /app
COPY ./app /app
 
RUN adduser -D user
USER user

docker-compose

Just to run our Dockerfile and a db image for PostgreSQL.

version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./app:/app
    command: >
      sh -c "python manage.py wait_for_db &&
             python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"
    environment:
      - DB_HOST=db
      - DB_NAME=app
      - DB_USER=postgres
      - DB_PASS=supersecretpassword
    depends_on:
      - db

  db:
    image: postgres:10-alpine
    environment:
      - POSTGRES_DB=app
      - POSRGRES_USER=postgres
      - POSTGRES_PASSWORD=supersecretpassword

wait_for_db

I have a command that waits for the DB to be available:

import time
 
from django.db import connections
from django.db.utils import OperationalError
from django.core.management.base import BaseCommand
 
 
class Command(BaseCommand):
    """Django command to pause execution until database is available"""
 
    def handle(self, *args, **options):
        self.stdout.write('Waiting for database...')
        db_conn = None
        while not db_conn:
            try:
                db_conn = connections['default']
            except OperationalError:
                self.stdout.write('Database unavailable, waiting 1 second...')
                time.sleep(1)
 
        self.stdout.write(self.style.SUCCESS('Database available!'))

And its unit test:

from unittest.mock import patch
 
from django.core.management import call_command
from django.db.utils import OperationalError
from django.test import TestCase
 
 
class CommandTests(TestCase):
 
    def test_wait_for_db_ready(self):
        """Test waiting for db when db is available"""
        with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
            gi.return_value = True
            call_command('wait_for_db')
            self.assertEqual(gi.call_count, 1)
 
    @patch('time.sleep', return_value=True)
    def test_wait_for_db(self, ts):
        """Test waiting for db"""
        with patch('django.db.utils.ConnectionHandler.__getitem__') as gi:
            gi.side_effect = [OperationalError] * 5 + [True]
            call_command('wait_for_db')
            self.assertEqual(gi.call_count, 6)

And that's it! The rest is just a regular Python/Django flow.

backend/drf-starter.txt · Last modified: 2019/11/19 23:17 (external edit)