├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── build_dev_example.sh ├── config └── nginx │ └── conf.d │ └── myproject.conf ├── docker-compose.yml ├── git-hooks ├── install_hooks.sh └── pre-commit └── src └── myproject ├── locale ├── .gitkeep └── de │ └── LC_MESSAGES │ └── django.po ├── manage.py ├── myproject ├── __init__.py ├── apps │ └── __init__.py ├── settings │ ├── __init__.py │ ├── _base.py │ ├── dev.py │ ├── last-update.txt │ ├── production.py │ ├── staging.py │ └── test.py ├── site_static │ └── site │ │ ├── css │ │ └── style.css │ │ ├── img │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ │ ├── js │ │ └── main.js │ │ └── scss │ │ └── style.scss ├── templates │ ├── base.html │ └── index.html ├── urls.py └── wsgi.py └── requirements ├── _base.txt ├── dev.txt ├── production.txt ├── staging.txt └── test.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # celery beat schedule file 95 | celerybeat-schedule 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env 102 | .venv 103 | env/ 104 | venv/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | # Media and Static directories 128 | /src/myproject/media/ 129 | !/src/myproject/media/.gitkeep 130 | 131 | /src/myproject/static/ 132 | !/src/myproject/static/.gitkeep 133 | 134 | # Building scripts with sensitive environment variables 135 | build_dev.sh 136 | build_production.sh 137 | build_staging.sh 138 | build_test.sh 139 | 140 | # PyCharm configuration 141 | .idea/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # pull official base image 2 | FROM python:3.8 3 | 4 | # accept arguments 5 | ARG PIP_REQUIREMENTS=production.txt 6 | 7 | # set environment variables 8 | ENV PYTHONDONTWRITEBYTECODE 1 9 | ENV PYTHONUNBUFFERED 1 10 | 11 | # install dependencies 12 | RUN pip install --upgrade pip setuptools 13 | 14 | # create user for the Django project 15 | RUN useradd -ms /bin/bash myproject 16 | 17 | # set current user 18 | USER myproject 19 | 20 | # set work directory 21 | WORKDIR /home/myproject 22 | 23 | # create and activate virtual environment 24 | RUN python3 -m venv env 25 | 26 | # copy and install pip requirements 27 | COPY --chown=myproject ./src/myproject/requirements /home/myproject/requirements/ 28 | RUN ./env/bin/pip3 install -r /home/myproject/requirements/${PIP_REQUIREMENTS} 29 | 30 | # copy Django project files 31 | COPY --chown=myproject ./src/myproject /home/myproject/ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Aidas Bendoraitis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django Docker 2 | 3 | ## 1. Create executable build_dev.sh 4 | 5 | Copy `build_dev_example.sh` to `build_dev.sh`. 6 | 7 | Edit the `build_dev.sh` file and add sensible values there. 8 | 9 | Add execution permissions: 10 | 11 | ```bash 12 | $ chmod +x build_dev.sh 13 | ``` 14 | 15 | ## 2. Build the Docker containers 16 | 17 | Run `build_dev.sh`: 18 | 19 | ```bash 20 | $ ./build_dev.sh 21 | ``` 22 | 23 | ## 3. Check if the build was successful 24 | 25 | If you now go to `http://0.0.0.0/` you should see a "Hello, World!" page there. 26 | 27 | If you now go to `http://0.0.0.0/admin/`, you should see 28 | 29 | ``` 30 | OperationalError at /admin/ 31 | FATAL: role "myproject" does not exist 32 | ``` 33 | 34 | This means that you have to create the database user and the database in the Docker container. 35 | 36 | ## 4. Create database user and project database 37 | 38 | SSH into the database container and create user and database there with the same values as in the `.build_dev.sh` script: 39 | 40 | ```bash 41 | $ docker exec -it django_docker_db_1 bash 42 | /# su - postgres 43 | /$ createuser --createdb --password myproject 44 | /$ createdb --username myproject myproject 45 | ``` 46 | 47 | When asked, enter the same password for the database as in the `build_dev.sh` script. 48 | 49 | Press [Ctrl + D] twice to logout of the postgres user and Docker container. 50 | 51 | If you now go to `http://0.0.0.0/admin/`, you should see 52 | 53 | ``` 54 | ProgrammingError at /admin/ 55 | relation "django_session" does not exist 56 | LINE 1: ...ession_data", "django_session"."expire_date" FROM "django_se... 57 | ``` 58 | 59 | This means that you have to run migrations to create database schema. 60 | 61 | ## 5. Run migration and collectstatic commands 62 | 63 | SSH into the gunicorn container and run the necessary Django management commands: 64 | 65 | ```bash 66 | $ docker exec -it django_docker_gunicorn_1 bash 67 | $ source env/bin/activate 68 | (env)$ python manage.py migrate 69 | (env)$ python manage.py collectstatic 70 | (env)$ python manage.py createsuperuser 71 | ``` 72 | 73 | Answer all the questions asked by the management commands. 74 | 75 | Press [Ctrl + D] twice to logout of the Docker container. 76 | 77 | If you now go to `http://0.0.0.0/admin/`, you should see the Django administration where you can login with the super user's credentials that you have just created. 78 | 79 | ## 6. Overview of useful commands 80 | 81 | ### Rebuild docker containers 82 | 83 | ```bash 84 | $ docker-compose down 85 | $ ./build_dev.sh 86 | ``` 87 | 88 | ### SSH to the Docker containers 89 | 90 | ```bash 91 | $ docker exec -it django_docker_gunicorn_1 bash 92 | $ docker exec -it django_docker_nginx_1 bash 93 | $ docker exec -it django_docker_db_1 bash 94 | ``` 95 | 96 | ### View logs 97 | 98 | ```bash 99 | $ docker-compose logs nginx 100 | $ docker-compose logs gunicorn 101 | $ docker-compose logs db 102 | ``` 103 | 104 | ### Copy files and directories to and from Docker container 105 | 106 | ```bash 107 | $ docker cp ~/avatar.png django_docker_gunicorn_1:/home/myproject/media/ 108 | $ docker cp django_docker_gunicorn_1:/home/myproject/media ~/Desktop/ 109 | ``` 110 | 111 | ## 7. Create analogous scripts for staging, production, and test environments 112 | 113 | Copy `build_dev.sh` to `build_staging.sh`, `build_production.sh`, and `build_test.sh` and change the environment variables analogously. 114 | 115 | ## 8. Feedback 116 | 117 | If you have any feedback about the boilerplate code or this README file, please open new issues. 118 | -------------------------------------------------------------------------------- /build_dev_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DJANGO_SETTINGS_MODULE=myproject.settings.dev \ 3 | DJANGO_SECRET_KEY="change-this-to-50-characters-long-random-string" \ 4 | DATABASE_NAME=myproject \ 5 | DATABASE_USER=myproject \ 6 | DATABASE_PASSWORD="change-this-too" \ 7 | EMAIL_HOST="localhost" \ 8 | EMAIL_PORT="25" \ 9 | EMAIL_HOST_USER="" \ 10 | EMAIL_HOST_PASSWORD="" \ 11 | PIP_REQUIREMENTS=dev.txt \ 12 | docker-compose up --detach --build 13 | -------------------------------------------------------------------------------- /config/nginx/conf.d/myproject.conf: -------------------------------------------------------------------------------- 1 | upstream myproject { 2 | server django_docker_gunicorn_1:8000; 3 | } 4 | 5 | server { 6 | 7 | listen 80; 8 | 9 | location / { 10 | proxy_pass http://myproject; 11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 12 | proxy_set_header Host $host; 13 | proxy_redirect off; 14 | } 15 | 16 | rewrite "/static/\d+/(.*)" /static/$1 last; 17 | 18 | location /static/ { 19 | alias /home/myproject/static/; 20 | } 21 | 22 | location /media/ { 23 | alias /home/myproject/media/; 24 | } 25 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | nginx: 5 | image: nginx:latest 6 | ports: 7 | - "80:80" 8 | volumes: 9 | - ./config/nginx/conf.d:/etc/nginx/conf.d 10 | - static_volume:/home/myproject/static 11 | - media_volume:/home/myproject/media 12 | depends_on: 13 | - gunicorn 14 | 15 | gunicorn: 16 | build: 17 | context: . 18 | args: 19 | PIP_REQUIREMENTS: "${PIP_REQUIREMENTS}" 20 | command: bash -c "/home/myproject/env/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 myproject.wsgi:application" 21 | depends_on: 22 | - db 23 | volumes: 24 | - static_volume:/home/myproject/static 25 | - media_volume:/home/myproject/media 26 | expose: 27 | - "8000" 28 | environment: 29 | DJANGO_SETTINGS_MODULE: "${DJANGO_SETTINGS_MODULE}" 30 | DJANGO_SECRET_KEY: "${DJANGO_SECRET_KEY}" 31 | DATABASE_NAME: "${DATABASE_NAME}" 32 | DATABASE_USER: "${DATABASE_USER}" 33 | DATABASE_PASSWORD: "${DATABASE_PASSWORD}" 34 | EMAIL_HOST: "${EMAIL_HOST}" 35 | EMAIL_PORT: "${EMAIL_PORT}" 36 | EMAIL_HOST_USER: "${EMAIL_HOST_USER}" 37 | EMAIL_HOST_PASSWORD: "${EMAIL_HOST_PASSWORD}" 38 | 39 | db: 40 | image: postgres:latest 41 | restart: always 42 | environment: 43 | POSTGRES_DB: "${DATABASE_NAME}" 44 | POSTGRES_USER: "${DATABASE_USER}" 45 | POSTGRES_PASSWORD: "${DATABASE_PASSWORD}" 46 | ports: 47 | - 5432 48 | volumes: 49 | - postgres_data:/var/lib/postgresql/data/ 50 | 51 | volumes: 52 | postgres_data: 53 | static_volume: 54 | media_volume: 55 | -------------------------------------------------------------------------------- /git-hooks/install_hooks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cp pre-commit ../.git/hooks/ -------------------------------------------------------------------------------- /git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from subprocess import check_output, CalledProcessError 3 | import os 4 | from datetime import datetime 5 | 6 | def root(): 7 | ''' returns the absolute path of the repository root ''' 8 | try: 9 | base = check_output(['git', 'rev-parse', '--show-toplevel']) 10 | except CalledProcessError: 11 | raise IOError('Current working directory is not a git repository') 12 | return base.decode('utf-8').strip() 13 | 14 | def abspath(relpath): 15 | ''' returns the absolute path for a path given relative to the root of 16 | the git repository 17 | ''' 18 | return os.path.join(root(), relpath) 19 | 20 | def add_to_git(file_path): 21 | ''' adds a file to git ''' 22 | try: 23 | base = check_output(['git', 'add', file_path]) 24 | except CalledProcessError: 25 | raise IOError('Current working directory is not a git repository') 26 | return base.decode('utf-8').strip() 27 | 28 | 29 | def main(): 30 | file_path = abspath("src/myproject/myproject/settings/last-update.txt") 31 | 32 | with open(file_path, 'w') as f: 33 | f.write(datetime.now().strftime("%Y%m%d%H%M%S")) 34 | 35 | add_to_git(file_path) 36 | 37 | if __name__ == '__main__': 38 | main() -------------------------------------------------------------------------------- /src/myproject/locale/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/locale/.gitkeep -------------------------------------------------------------------------------- /src/myproject/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2019-08-18 11:08+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: myproject/templates/base.html:15 myproject/templates/index.html:5 22 | msgid "Hello, World!" 23 | msgstr "Hallo, Welt!" 24 | -------------------------------------------------------------------------------- /src/myproject/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /src/myproject/myproject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/__init__.py -------------------------------------------------------------------------------- /src/myproject/myproject/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/apps/__init__.py -------------------------------------------------------------------------------- /src/myproject/myproject/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/settings/__init__.py -------------------------------------------------------------------------------- /src/myproject/myproject/settings/_base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for myproject project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2.3. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | from django.core.exceptions import ImproperlyConfigured 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 18 | 19 | 20 | def get_secret(setting): 21 | """Get the secret variable or return explicit exception.""" 22 | try: 23 | return os.environ[setting] 24 | except KeyError: 25 | error_msg = f'Set the {setting} environment variable' 26 | raise ImproperlyConfigured(error_msg) 27 | 28 | 29 | # Quick-start development settings - unsuitable for production 30 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 31 | 32 | # SECURITY WARNING: keep the secret key used in production secret! 33 | SECRET_KEY = get_secret('DJANGO_SECRET_KEY') 34 | 35 | # SECURITY WARNING: don't run with debug turned on in production! 36 | DEBUG = True 37 | 38 | ALLOWED_HOSTS = [ 39 | "127.0.0.1", 40 | "0.0.0.0", 41 | ] 42 | 43 | 44 | # Application definition 45 | 46 | INSTALLED_APPS = [ 47 | # contributed 48 | 'django.contrib.admin', 49 | 'django.contrib.auth', 50 | 'django.contrib.contenttypes', 51 | 'django.contrib.sessions', 52 | 'django.contrib.messages', 53 | 'django.contrib.staticfiles', 54 | # third-party 55 | # ... 56 | # local 57 | # ... 58 | ] 59 | 60 | MIDDLEWARE = [ 61 | 'django.middleware.security.SecurityMiddleware', 62 | 'django.contrib.sessions.middleware.SessionMiddleware', 63 | 'django.middleware.common.CommonMiddleware', 64 | 'django.middleware.csrf.CsrfViewMiddleware', 65 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 66 | 'django.contrib.messages.middleware.MessageMiddleware', 67 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 68 | 'django.middleware.locale.LocaleMiddleware', 69 | ] 70 | 71 | ROOT_URLCONF = 'myproject.urls' 72 | 73 | TEMPLATES = [ 74 | { 75 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 76 | 'DIRS': [os.path.join(BASE_DIR, 'myproject', 'templates')], 77 | 'APP_DIRS': True, 78 | 'OPTIONS': { 79 | 'context_processors': [ 80 | 'django.template.context_processors.debug', 81 | 'django.template.context_processors.request', 82 | 'django.contrib.auth.context_processors.auth', 83 | 'django.contrib.messages.context_processors.messages', 84 | ], 85 | }, 86 | }, 87 | ] 88 | 89 | WSGI_APPLICATION = 'myproject.wsgi.application' 90 | 91 | 92 | # Database 93 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 94 | 95 | DATABASES = { 96 | 'default': { 97 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 98 | 'NAME': get_secret('DATABASE_NAME'), 99 | 'USER': get_secret('DATABASE_USER'), 100 | 'PASSWORD': get_secret('DATABASE_PASSWORD'), 101 | 'HOST': 'db', 102 | 'PORT': '5432', 103 | }} 104 | 105 | 106 | # Password validation 107 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 108 | 109 | AUTH_PASSWORD_VALIDATORS = [ 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 112 | }, 113 | { 114 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 115 | }, 116 | { 117 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 118 | }, 119 | { 120 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 121 | }, 122 | ] 123 | 124 | 125 | # Internationalization 126 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 127 | 128 | LANGUAGE_CODE = 'en-us' 129 | 130 | TIME_ZONE = 'UTC' 131 | 132 | USE_I18N = True 133 | 134 | USE_L10N = True 135 | 136 | USE_TZ = True 137 | 138 | LOCALE_PATHS = [ 139 | os.path.join(BASE_DIR, 'locale'), 140 | ] 141 | 142 | # Static files (CSS, JavaScript, Images) 143 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 144 | 145 | STATICFILES_DIRS = [ 146 | os.path.join(BASE_DIR, 'myproject', 'site_static'), 147 | ] 148 | 149 | with open(os.path.join(BASE_DIR, 'myproject', 'settings', 'last-update.txt'), 'r') as f: 150 | timestamp = f.readline().strip() 151 | 152 | STATIC_URL = f'/static/{timestamp}/' 153 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 154 | 155 | MEDIA_URL = '/media/' 156 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 157 | 158 | EMAIL_HOST = get_secret("EMAIL_HOST") 159 | EMAIL_PORT = get_secret("EMAIL_PORT") 160 | EMAIL_HOST_USER = get_secret("EMAIL_HOST_USER") 161 | EMAIL_HOST_PASSWORD = get_secret("EMAIL_HOST_PASSWORD") 162 | -------------------------------------------------------------------------------- /src/myproject/myproject/settings/dev.py: -------------------------------------------------------------------------------- 1 | from ._base import * 2 | 3 | DEBUG = True 4 | -------------------------------------------------------------------------------- /src/myproject/myproject/settings/last-update.txt: -------------------------------------------------------------------------------- 1 | 20200122070002 -------------------------------------------------------------------------------- /src/myproject/myproject/settings/production.py: -------------------------------------------------------------------------------- 1 | from ._base import * 2 | 3 | DEBUG = False 4 | -------------------------------------------------------------------------------- /src/myproject/myproject/settings/staging.py: -------------------------------------------------------------------------------- 1 | from ._base import * 2 | 3 | DEBUG = False 4 | -------------------------------------------------------------------------------- /src/myproject/myproject/settings/test.py: -------------------------------------------------------------------------------- 1 | from ._base import * 2 | 3 | DEBUG = True 4 | -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/css/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/css/style.css -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/img/favicon-16x16.png -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/img/favicon-32x32.png -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/img/favicon.ico -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/js/main.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/js/main.js -------------------------------------------------------------------------------- /src/myproject/myproject/site_static/site/scss/style.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archatas/django_docker/d5459a3e98c4e7a8d251c225f435326d79e8dc54/src/myproject/myproject/site_static/site/scss/style.scss -------------------------------------------------------------------------------- /src/myproject/myproject/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% load i18n static %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {% trans "Hello, World!" %} 16 | 17 | 18 |
19 | {% block content %} 20 | {% endblock %} 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/myproject/myproject/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

{% trans "Hello, World!" %}

6 | {% endblock %} -------------------------------------------------------------------------------- /src/myproject/myproject/urls.py: -------------------------------------------------------------------------------- 1 | """myproject URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls.i18n import i18n_patterns 17 | from django.contrib import admin 18 | from django.urls import path 19 | from django.views.generic import TemplateView 20 | 21 | 22 | urlpatterns = i18n_patterns( 23 | path('', TemplateView.as_view(template_name="index.html")), 24 | path('admin/', admin.site.urls), 25 | ) 26 | -------------------------------------------------------------------------------- /src/myproject/myproject/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for myproject 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/2.2/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', 'myproject.settings.production') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /src/myproject/requirements/_base.txt: -------------------------------------------------------------------------------- 1 | pip~=20.0 2 | Django~=3.0.0 3 | gunicorn==20.0.4 4 | psycopg2-binary==2.8.3 5 | pytz==2019.1 6 | sqlparse==0.3.0 7 | -------------------------------------------------------------------------------- /src/myproject/requirements/dev.txt: -------------------------------------------------------------------------------- 1 | -r _base.txt 2 | coverage==4.5.3 3 | -------------------------------------------------------------------------------- /src/myproject/requirements/production.txt: -------------------------------------------------------------------------------- 1 | -r _base.txt 2 | -------------------------------------------------------------------------------- /src/myproject/requirements/staging.txt: -------------------------------------------------------------------------------- 1 | -r _base.txt 2 | -------------------------------------------------------------------------------- /src/myproject/requirements/test.txt: -------------------------------------------------------------------------------- 1 | -r _base.txt 2 | coverage==4.5.3 3 | --------------------------------------------------------------------------------