Published on

Step by step migration of django cookiecutter postgres to mysql 8+

Authors

"Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly."

So, you recently discovered django-cookiecutter and fell in love with a rather opinionated workflow on how files and services are organized but soon you release that django-cookiecutter does not have support for MySQL databases!

There have been some open issues [1] [2] in their repository but they don't provide an out of the box solution to support MySQL.

Prerequisites

I will assume that you already have installed cookiecutter and have run the below command to initialize your project.

$ cookiecutter https://github.com/pydanny/cookiecutter-django

After the project is bootstrapped, you'd be provided with local.yml file to run your project with docker-compose. We are going to make some changes into the local.yml.

  1. Replace the highlighted blocks with the below code snippet.
# local.yml

version: '3'

services:
  ...
  django:
    ...
    depends_on:
      - postgres
    ...

  postgres:
    build:
      context: .
      dockerfile: ./compose/production/postgres/Dockerfile
    image: my_awesome_project_production_postgres
    container_name: postgres
    volumes:
      - local_postgres_data:/var/lib/postgresql/data:Z
      - local_postgres_data_backups:/backups:z
    env_file:
      - ./.envs/.local/.postgres

  ...

version: '3'

services:
  ...

  django:
    ...
    depends_on:
      - mysql
    ...

  mysql:
      image: mysql:8.0
      command: --default-authentication-plugin=mysql_native_password --mysqlx=0
      container_name: my_awesome_project_local_mysql
      volumes:
        - "./db:/var/lib/mysql"
      ports:
        - '3306:3306'
      env_file:
        - ./.envs/.local/.mysql

  ...

  1. Create a new file at ./.envs/.local/.mysql and paste the following environment variables
# MYSQL

MYSQL_HOST=mysql
MYSQL_PORT=3306
MYSQL_DATABASE=<name-of-your-database>
MYSQL_USER=<name-of-your-mysql-user>
MYSQL_PASSWORD=<some-random-password>
MYSQL_ROOT_PASSWORD=<some-root-random-password>
DATABASE_URL=mysql://<name-of-your-mysql-user>:<some-random-password>@mysql:3306/<name-of-your-database>
  1. Edit the file located in ./compose/local/django/Dockerfile and add the highlighted line
# ./compose/local/django/Dockerfile

FROM python:3.8-slim-buster

...

RUN apt-get update \
  # dependencies for building Python packages
  && apt-get install -y build-essential \
  # psycopg2 dependencies
  && apt-get install -y libpq-dev \
  # install mysql client
  && apt-get install -y default-libmysqlclient-dev \
  # Translations dependencies
  && apt-get install -y gettext \
  # cleaning up unused files
  && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
  && rm -rf /var/lib/apt/lists/*

...

COPY ./compose/production/django/entrypoint /entrypoint

...

ENTRYPOINT ["/entrypoint"]


  1. If you have other docker services that uses django, for example docs service in DockerFile. Edit the Dockerfile to install mysql libraries into those container services too.

  2. replace the content of ./compose/production/django/entrypoint (why production? because of entrypoint mentioned in ./compose/local/django/Dockerfile) with below code snippet to make your django container aware when your database is ready for connection

# ./compose/production/django/entrypoint

#!/bin/bash

set -o errexit
set -o pipefail
set -o nounset



export DATABASE_URL="mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}"

echo $DATABASE_URL

mysql_ready() {
python << END
import sys
import mysql.connector

try:
    mysql.connector.connect(
      host="${MYSQL_HOST}",
      user="${MYSQL_USER}",
      password="${MYSQL_PASSWORD}",
    )
except Exception as e:
    print('Exception at entrypoint file', e)
    sys.exit(-1)
sys.exit(0)

END
}
until mysql_ready; do
  >&2 echo 'Waiting for MySQL to become available...'
  sleep 1
done
>&2 echo 'Mysql is available'

exec "$@"

  1. Add the requirement in pip's requirement file ./requirements/base.txt

...
mysql-connector-python==8.0.22
mysqlclient==2.0.1
...

  1. Run the docker-compose

At this point of time, your project should be able to run below commands successfully

$ docker-compose -f local.yml build
$ docker-compose -f local.yml up

##Changes for production.yml

Follow the similar approach to edit the required files to setup your production.yml to work for mysql container service.