├── web ├── tests │ ├── __init__.py │ └── test_env_settings.py ├── docker_django │ ├── __init__.py │ ├── apps │ │ ├── __init__.py │ │ └── todo │ │ │ ├── __init__.py │ │ │ ├── tests.py │ │ │ ├── admin.py │ │ │ ├── urls.py │ │ │ ├── models.py │ │ │ ├── views.py │ │ │ └── templates │ │ │ ├── _base.html │ │ │ └── home.html │ ├── urls.py │ ├── wsgi.py │ └── settings.py ├── static │ └── main.css ├── requirements.txt ├── Dockerfile └── manage.py ├── .gitignore ├── nginx ├── Dockerfile └── sites-enabled │ └── django_project ├── .env ├── README.md ├── production.yml └── docker-compose.yml /web/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/docker_django/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/docker_django/apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/static/main.css: -------------------------------------------------------------------------------- 1 | /* custom styles */ 2 | 3 | .container { 4 | max-width: 500px; 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | .DS_Store 3 | .idea 4 | venv 5 | env 6 | __pycache__ 7 | .venv 8 | .cache 9 | -------------------------------------------------------------------------------- /web/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.1.7 2 | gunicorn==19.9.0 3 | psycopg2==2.7.7 4 | redis==3.2.1 5 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tutum/nginx 2 | 3 | RUN rm /etc/nginx/sites-enabled/default 4 | 5 | COPY sites-enabled/ /etc/nginx/sites-enabled 6 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | re_path(r'^$', views.home, name='home'), 7 | ] 8 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | 3 | RUN python -m pip install --upgrade pip 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN python -m pip install -r requirements.txt 7 | 8 | COPY . . 9 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Add Environment Variables 2 | 3 | 4 | SECRET_KEY=5(15ds+i2+%ik6z&!yer+ga9m=e%jcqiz_5wszg)r-z!2--b2d 5 | DB_NAME=postgres 6 | DB_USER=postgres 7 | DB_PASS=postgres 8 | DB_SERVICE=postgres 9 | DB_PORT=5432 -------------------------------------------------------------------------------- /web/docker_django/apps/todo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Item(models.Model): 5 | text = models.TextField(blank=False, null=False) 6 | date_posted = models.DateField(auto_now=True) 7 | -------------------------------------------------------------------------------- /web/docker_django/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, re_path 2 | from django.contrib import admin 3 | 4 | urlpatterns = [ 5 | re_path(r'^admin/', admin.site.urls), 6 | re_path(r'^', include('docker_django.apps.todo.urls')), 7 | ] 8 | -------------------------------------------------------------------------------- /web/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "docker_django.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /nginx/sites-enabled/django_project: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name example.org; 5 | charset utf-8; 6 | 7 | location /static { 8 | alias /usr/src/app/static; 9 | } 10 | 11 | location / { 12 | proxy_pass http://web:8000; 13 | proxy_set_header Host $host; 14 | proxy_set_header X-Real-IP $remote_addr; 15 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /web/docker_django/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for docker_django project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "docker_django.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from .models import Item 3 | from redis import Redis 4 | 5 | 6 | redis = Redis(host='redis', port=6379) 7 | 8 | 9 | def home(request): 10 | if request.method == 'POST': 11 | Item.objects.create(text=request.POST['item_text']) 12 | return redirect('/') 13 | items = Item.objects.all() 14 | counter = redis.incr('counter') 15 | return render(request, 'home.html', {'items': items, 'counter': counter}) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Django Development With Docker Compose and Machine 2 | 3 | Featuring: 4 | 5 | - Docker v18.09.2 6 | - Docker Compose v1.23.2 7 | - Docker Machine v0.16.1 8 | - Python 3.7.3 9 | 10 | Blog post -> https://realpython.com/blog/python/django-development-with-docker-compose-and-machine/ 11 | 12 | ### OS X Instructions 13 | 14 | 1. Start new machine - `docker-machine create -d virtualbox dev;` 15 | 1. Configure your shell to use the new machine environment - `eval $(docker-machine env dev)` 16 | 1. Build images - `docker-compose build` 17 | 1. Start services - `docker-compose up -d` 18 | 1. Create migrations - `docker-compose run web /usr/local/bin/python manage.py migrate` 19 | 1. Grab IP - `docker-machine ip dev` - and view in your browser 20 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/templates/_base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | Dockerizing Django! 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | {% block content %} 14 | {% endblock %} 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /web/docker_django/apps/todo/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "_base.html" %} 2 | 3 | 4 | {% block content %} 5 | 6 |
7 | {% load staticfiles %} 8 |
9 | 10 |
11 |

This page has been viewed {{counter}} times!

12 |
13 | 14 |
15 | 16 |
17 | {% csrf_token %} 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | {% for item in items %} 29 |

{{item.text}} - {{item.date_posted}}

30 | {% endfor %} 31 |
32 | 33 | {% endblock content %} -------------------------------------------------------------------------------- /production.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | web: 5 | restart: always 6 | build: ./web 7 | expose: 8 | - "8000" 9 | links: 10 | - postgres:postgres 11 | - redis:redis 12 | volumes: 13 | - web-static:/usr/src/app/static 14 | env_file: .env 15 | command: /usr/local/bin/gunicorn docker_django.wsgi:application -w 2 -b :8000 16 | 17 | nginx: 18 | restart: always 19 | build: ./nginx/ 20 | ports: 21 | - "80:80" 22 | volumes: 23 | - web-static:/www/static 24 | links: 25 | - web:web 26 | 27 | postgres: 28 | restart: always 29 | image: postgres:latest 30 | ports: 31 | - "5432" 32 | volumes: 33 | - pgdata:/var/lib/postgresql/data/ 34 | 35 | redis: 36 | restart: always 37 | image: redis:latest 38 | ports: 39 | - "6379" 40 | volumes: 41 | - redisdata:/data 42 | 43 | volumes: 44 | web-static: 45 | pgdata: 46 | redisdata: 47 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | web: 5 | restart: always 6 | build: ./web 7 | expose: 8 | - "8000" 9 | links: 10 | - postgres:postgres 11 | - redis:redis 12 | volumes: 13 | - web-django:/usr/src/app 14 | - web-static:/usr/src/app/static 15 | env_file: .env 16 | environment: 17 | DEBUG: 'true' 18 | command: /usr/local/bin/gunicorn docker_django.wsgi:application -w 2 -b :8000 19 | 20 | nginx: 21 | restart: always 22 | build: ./nginx/ 23 | ports: 24 | - "80:80" 25 | volumes: 26 | - web-static:/www/static 27 | links: 28 | - web:web 29 | 30 | postgres: 31 | restart: always 32 | image: postgres:latest 33 | ports: 34 | - "5432:5432" 35 | volumes: 36 | - pgdata:/var/lib/postgresql/data/ 37 | 38 | redis: 39 | restart: always 40 | image: redis:latest 41 | ports: 42 | - "6379:6379" 43 | volumes: 44 | - redisdata:/data 45 | 46 | volumes: 47 | web-django: 48 | web-static: 49 | pgdata: 50 | redisdata: 51 | -------------------------------------------------------------------------------- /web/tests/test_env_settings.py: -------------------------------------------------------------------------------- 1 | """Test Environmental settings are handled properly.""" 2 | 3 | 4 | import os 5 | import importlib 6 | from unittest.mock import patch 7 | # from django.test import TestCase 8 | # from unittest import skip 9 | # we have to use tools outside of django, because when it's initialized 10 | # it's too late to change environment variables 11 | from unittest import TestCase, main 12 | 13 | 14 | class DebugSettingTest(TestCase): 15 | """Test if setting DEBUG is handled properly.""" 16 | 17 | _variants = { 18 | True: ('Yes', 'YES', 'Y', 'TRUE', 'tRUE', 'true', 'On'), 19 | 20 | False: ('No', 'nO', 'N', 'n', 'false', 'False', 'off', 'oFF'), 21 | } 22 | env_var_debug = 'DEBUG' 23 | 24 | def test_debug_setting(self): 25 | """Check if config accepts environment variable DEBUG and sets it.""" 26 | from docker_django import settings 27 | for result, words in self._variants.items(): 28 | for word in words: 29 | # print(word, result) 30 | with patch.dict('os.environ', {self.env_var_debug: word}): 31 | importlib.reload(settings) 32 | assert self.env_var_debug in os.environ 33 | self.assertEqual(settings.DEBUG, result) 34 | assert self.env_var_debug not in os.environ # should be True 35 | 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /web/docker_django/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for docker_django project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = os.environ['SECRET_KEY'] 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = os.getenv('DEBUG', 'NO').lower() in ('on', 'true', 'y', 'yes') 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | # apps 41 | 'docker_django.apps.todo', 42 | ) 43 | 44 | MIDDLEWARE_CLASSES = ( 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | 'django.middleware.security.SecurityMiddleware', 53 | ) 54 | 55 | ROOT_URLCONF = 'docker_django.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': [], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'docker_django.wsgi.application' 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 78 | 79 | # DATABASES = { 80 | # 'default': { 81 | # 'ENGINE': 'django.db.backends.sqlite3', 82 | # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 83 | # } 84 | # } 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 89 | 'NAME': os.environ['DB_NAME'], 90 | 'USER': os.environ['DB_USER'], 91 | 'PASSWORD': os.environ['DB_PASS'], 92 | 'HOST': os.environ['DB_SERVICE'], 93 | 'PORT': os.environ['DB_PORT'] 94 | } 95 | } 96 | 97 | # Internationalization 98 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 99 | 100 | LANGUAGE_CODE = 'en-us' 101 | 102 | TIME_ZONE = 'UTC' 103 | 104 | USE_I18N = True 105 | 106 | USE_L10N = True 107 | 108 | USE_TZ = True 109 | 110 | 111 | # Static files (CSS, JavaScript, Images) 112 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 113 | 114 | STATIC_URL = '/static/' 115 | # STATICFILES_DIRS = ( 116 | # os.path.join(BASE_DIR, 'static'), 117 | # ) 118 | # print(STATICFILES_DIRS) 119 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 120 | --------------------------------------------------------------------------------