├── .envs ├── .django └── .postgres ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── Makefile ├── compose ├── django │ └── Dockerfile └── postgres │ ├── Dockerfile │ └── maintenance │ ├── _sourced │ ├── constants.sh │ ├── countdown.sh │ ├── messages.sh │ └── yes_no.sh │ ├── backup │ ├── backups │ └── restore ├── config └── nginx │ └── mydjango.conf ├── docker-compose.yml ├── readme.md └── src ├── manage.py ├── myapp ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── tasks.py ├── tests.py └── views.py ├── mydjango ├── __init__.py ├── celery.py ├── settings.py ├── urls.py └── wsgi.py ├── requirements.pip └── templates └── hello_world.html /.envs/.django: -------------------------------------------------------------------------------- 1 | DB_HOST=db 2 | DB_PORT=5432 3 | DB_PASSWORD=password 4 | DB_USER=user 5 | DB_NAME=django_db 6 | -------------------------------------------------------------------------------- /.envs/.postgres: -------------------------------------------------------------------------------- 1 | POSTGRES_HOST=db 2 | POSTGRES_PORT=5432 3 | POSTGRES_USER=user 4 | POSTGRES_PASSWORD=password 5 | POSTGRES_DB=django_db 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: ruddra 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | .vscode 4 | */*.pyc 5 | */*/*.pyc 6 | */migrations/* 7 | ./src/static/* 8 | /__pycache__ 9 | src/mydjango/__pycache__/settings.cpython-35.pyc 10 | */static/ 11 | .DS_Store 12 | .env 13 | **/.DS_Store 14 | src/mydjango/local_settings.py 15 | src/media -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ruddra 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker-compose build 3 | 4 | up: 5 | docker-compose up -d 6 | 7 | up-non-daemon: 8 | docker-compose up 9 | 10 | start: 11 | docker-compose start 12 | 13 | stop: 14 | docker-compose stop 15 | 16 | restart: 17 | docker-compose stop && docker-compose start 18 | 19 | shell-nginx: 20 | docker exec -ti nz01 /bin/sh 21 | 22 | shell-web: 23 | docker exec -ti dz01 /bin/sh 24 | 25 | shell-db: 26 | docker exec -ti pz01 /bin/sh 27 | 28 | log-nginx: 29 | docker-compose logs nginx 30 | 31 | log-web: 32 | docker-compose logs web 33 | 34 | log-db: 35 | docker-compose logs db 36 | 37 | collectstatic: 38 | docker exec dz01 /bin/sh -c "python manage.py collectstatic --noinput" -------------------------------------------------------------------------------- /compose/django/Dockerfile: -------------------------------------------------------------------------------- 1 | # More Datascience frendly gist can be found here: https://gist.github.com/ruddra/870d7a51238ddfa4b50375086c12a4f5 2 | # pull official python alpine image 3 | FROM python:3.7-alpine 4 | 5 | # Set Environment Variable 6 | ENV PYTHONUNBUFFERED 1 7 | ENV C_FORCE_ROOT true 8 | 9 | # Making source and static directory 10 | RUN mkdir /src 11 | RUN mkdir /static 12 | 13 | # Creating Work Directory 14 | WORKDIR /src 15 | 16 | # Adding mandatory packages to docker 17 | RUN apk update && apk add --no-cache \ 18 | postgresql \ 19 | zlib \ 20 | jpeg 21 | # un-comment the following two dependecies if you want to add library like pandas, scipy and numpy 22 | # openblas \ 23 | # libstdc++ 24 | 25 | # Installing temporary packages required for installing requirements.pip 26 | RUN apk add --no-cache --virtual build-deps \ 27 | gcc \ 28 | python3-dev \ 29 | musl-dev \ 30 | postgresql-dev\ 31 | zlib-dev \ 32 | jpeg-dev 33 | # un-comment if you want to install numpy, pandas, scipy etc and their supported dependencies 34 | # g++ \ 35 | # openblas-dev \ 36 | # cmake \ 37 | # && ln -s /usr/include/locale.h /usr/include/xlocale.h 38 | 39 | # Update pip 40 | RUN pip install --upgrade pip 41 | 42 | # **if you want to install scipy uncomment the following file** 43 | # RUN pip3 install --no-cache-dir --disable-pip-version-check scipy==1.3.1 44 | 45 | # Installing requirements.pip from project 46 | COPY ./src/requirements.pip /scripts/ 47 | RUN pip install --no-cache-dir -r /scripts/requirements.pip 48 | 49 | # *install psycopg2 if you don't have it requirements.pip* 50 | # RUN pip install --no-cache-dir psycopg2 51 | 52 | # removing temporary packages from docker and removing cache 53 | RUN apk del build-deps && \ 54 | find -type d -name __pycache__ -prune -exec rm -rf {} \; && \ 55 | rm -rf ~/.cache/pip 56 | 57 | RUN addgroup --system django && adduser --system django --ingroup django 58 | # COPY --from=base /opt/venv /opt/venv 59 | # ENV PATH="/opt/venv/bin:$PATH" 60 | USER django 61 | # CMD will run when this dockerfile is running 62 | CMD ["sh", "-c", "python manage.py collectstatic --no-input; python manage.py migrate; gunicorn mydjango.wsgi -b 0.0.0.0:8000 & celery --app myapp.tasks worker --loglevel=INFO"] 63 | -------------------------------------------------------------------------------- /compose/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:11-alpine 2 | 3 | COPY ./compose/postgres/maintenance /usr/local/bin/maintenance 4 | RUN chmod +x /usr/local/bin/maintenance/* 5 | RUN mv /usr/local/bin/maintenance/* /usr/local/bin && rmdir /usr/local/bin/maintenance -------------------------------------------------------------------------------- /compose/postgres/maintenance/_sourced/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | BACKUP_DIR_PATH='/backups' 5 | BACKUP_FILE_PREFIX='backup' 6 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/_sourced/countdown.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | countdown() { 5 | declare desc="A simple countdown. Source: https://superuser.com/a/611582" 6 | local seconds="${1}" 7 | local d=$(($(date +%s) + "${seconds}")) 8 | while [ "$d" -ge `date +%s` ]; do 9 | echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r"; 10 | sleep 0.1 11 | done 12 | } 13 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/_sourced/messages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | message_newline() { 5 | echo 6 | } 7 | 8 | message_debug() 9 | { 10 | echo -e "DEBUG: ${@}" 11 | } 12 | 13 | message_welcome() 14 | { 15 | echo -e "\e[1m${@}\e[0m" 16 | } 17 | 18 | message_warning() 19 | { 20 | echo -e "\e[33mWARNING\e[0m: ${@}" 21 | } 22 | 23 | message_error() 24 | { 25 | echo -e "\e[31mERROR\e[0m: ${@}" 26 | } 27 | 28 | message_info() 29 | { 30 | echo -e "\e[37mINFO\e[0m: ${@}" 31 | } 32 | 33 | message_suggestion() 34 | { 35 | echo -e "\e[33mSUGGESTION\e[0m: ${@}" 36 | } 37 | 38 | message_success() 39 | { 40 | echo -e "\e[32mSUCCESS\e[0m: ${@}" 41 | } 42 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/_sourced/yes_no.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | yes_no() { 5 | declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message." 6 | local arg1="${1}" 7 | 8 | local response= 9 | read -r -p "${arg1} (y/[n])? " response 10 | if [[ "${response}" =~ ^[Yy]$ ]] 11 | then 12 | exit 0 13 | else 14 | exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Create a database backup. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backup 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "Backing up the '${POSTGRES_DB}' database..." 21 | 22 | 23 | if [[ "${POSTGRES_USER}" == "postgres" ]]; then 24 | message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." 25 | exit 1 26 | fi 27 | 28 | export PGHOST="${POSTGRES_HOST}" 29 | export PGPORT="${POSTGRES_PORT}" 30 | export PGUSER="${POSTGRES_USER}" 31 | export PGPASSWORD="${POSTGRES_PASSWORD}" 32 | export PGDATABASE="${POSTGRES_DB}" 33 | 34 | backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz" 35 | pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}" 36 | 37 | 38 | message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'." 39 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/backups: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### View backups. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backups 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "These are the backups you have got:" 21 | 22 | ls -lht "${BACKUP_DIR_PATH}" 23 | -------------------------------------------------------------------------------- /compose/postgres/maintenance/restore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Restore database from a backup. 5 | ### 6 | ### Parameters: 7 | ### <1> filename of an existing backup. 8 | ### 9 | ### Usage: 10 | ### $ docker-compose -f .yml (exec |run --rm) postgres restore <1> 11 | 12 | 13 | set -o errexit 14 | set -o pipefail 15 | set -o nounset 16 | 17 | 18 | working_dir="$(dirname ${0})" 19 | source "${working_dir}/_sourced/constants.sh" 20 | source "${working_dir}/_sourced/messages.sh" 21 | 22 | 23 | if [[ -z ${1+x} ]]; then 24 | message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again." 25 | exit 1 26 | fi 27 | backup_filename="${BACKUP_DIR_PATH}/${1}" 28 | if [[ ! -f "${backup_filename}" ]]; then 29 | message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again." 30 | exit 1 31 | fi 32 | 33 | message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..." 34 | 35 | if [[ "${POSTGRES_USER}" == "postgres" ]]; then 36 | message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." 37 | exit 1 38 | fi 39 | 40 | export PGHOST="${POSTGRES_HOST}" 41 | export PGPORT="${POSTGRES_PORT}" 42 | export PGUSER="${POSTGRES_USER}" 43 | export PGPASSWORD="${POSTGRES_PASSWORD}" 44 | export PGDATABASE="${POSTGRES_DB}" 45 | 46 | message_info "Dropping the database..." 47 | dropdb "${PGDATABASE}" 48 | 49 | message_info "Creating a new database..." 50 | createdb --owner="${POSTGRES_USER}" 51 | 52 | message_info "Applying the backup to the new database..." 53 | gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}" 54 | 55 | message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup." 56 | -------------------------------------------------------------------------------- /config/nginx/mydjango.conf: -------------------------------------------------------------------------------- 1 | client_max_body_size 10M; 2 | 3 | upstream web { 4 | ip_hash; 5 | server web:8000; 6 | } 7 | 8 | server { 9 | 10 | location /static/ { 11 | autoindex on; 12 | alias /src/static/; 13 | } 14 | 15 | location /media/ { 16 | autoindex on; 17 | alias /src/media/; 18 | } 19 | 20 | location / { 21 | proxy_pass http://web/; 22 | } 23 | listen 8000; 24 | server_name localhost; 25 | } 26 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | volumes: 4 | local_postgres_data: {} 5 | local_postgres_data_backups: {} 6 | 7 | services: 8 | nginx: 9 | image: nginx:alpine 10 | container_name: nz01 11 | ports: 12 | - "8000:8000" 13 | volumes: 14 | - ./src:/src 15 | - ./config/nginx:/etc/nginx/conf.d 16 | depends_on: 17 | - web 18 | networks: 19 | - djangonetwork 20 | web: 21 | build: 22 | context: . 23 | dockerfile: compose/django/Dockerfile 24 | container_name: dz01 25 | depends_on: 26 | - db 27 | volumes: 28 | - ./src:/src 29 | expose: 30 | - "8000" 31 | links: 32 | - redis 33 | env_file: 34 | - ./.envs/.django 35 | networks: 36 | - djangonetwork 37 | db: 38 | build: 39 | context: . 40 | dockerfile: compose/postgres/Dockerfile 41 | container_name: pz01 42 | env_file: 43 | - ./.envs/.postgres 44 | volumes: 45 | - local_postgres_data:/var/lib/postgresql/data 46 | - local_postgres_data_backups:/backups 47 | networks: 48 | - djangonetwork 49 | redis: 50 | image: redis:alpine 51 | container_name: rz01 52 | ports: 53 | - "6379:6379" 54 | networks: 55 | - djangonetwork 56 | 57 | networks: 58 | djangonetwork: 59 | driver: bridge 60 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Deploy Django using Nginx, Celery, Redis and Postgresql with Docker 2 | 3 | A boilerplate to deploy Django with cool stuff. Also serves as an example project from these tutorial: 4 | 1. Deploy Django, Gunicorn, NGINX, Postgresql using Docker 5 | 2. Serve Static Files by Nginx from Django using Docker 6 | 3. Docker: Use Celery in Django(Redis as Broker) 7 | 8 | Where it is described how this boilerplate was created from scratch so that you can build your own. 9 | 10 | ## Advantages 11 | 1. Ready to use with your django project. 12 | 2. Combined with NGINX, Redis, Celery to handle relevent things. 13 | 3. Alpine based images are used, so that sizes of the images are compartively low. 14 | 4. Now comes built it with Numpy, Scipy and Pandas support. So you can integrate your datascience projects with this. [Instructions](#now-featuring-numpy-scipy-and-pandas) for integrating these libraries are also shared in the `Dockerfile`. 15 | 5. With Numpy, Pandas and Scipy dependecies installed, the total size is 657MB(may differ if you have more packages). Without these, size reduces to 390MB. 16 | 6. Now comes with support to install [Pillow](https://pypi.org/project/Pillow/) using django. 17 | 18 | ## Now Featuring Numpy, Scipy and Pandas 19 | In the [**`Dockerfile`**](https://github.com/ruddra/docker-django/blob/master/compose/django/Dockerfile), there are detailed instructions on how to install data science dependencies. 20 | 21 | **PS:** Here is a [**__`gist`__**](https://gist.github.com/ruddra/870d7a51238ddfa4b50375086c12a4f5) which is more useful for Numpy, Pandas, Scipy etc. And it is usable with this project's [`docker-compose.yml`](https://github.com/ruddra/docker-django/blob/master/docker-compose.yml) file. Just you need to replace the `Dockerfile` from [*./compose*](https://github.com/ruddra/docker-django/blob/master/compose) directory with the one in the *gist*. 22 | 23 | ## Basic Usage 24 | 1. First run **`make build`** inside root directory. 25 | 2. Then run **`make up`** to start up the project for first time. 26 | 3. Use/update environment variables from [**`.envs`**](https://github.com/ruddra/docker-django/blob/master/.envs) folder. 27 | 28 | Checkout the [commands](#commands) section for more usage. 29 | 30 | ## Preview 31 | A default Django project resides in `src` directory. So, when you start the project, you will see the following screen in `8000` port: 32 | 33 | ![Demo One](https://github.com/ruddra/blog-images/raw/master/Demo%201.png) 34 | 35 | Also when you access the django container log via `make log-web`, you will see the following: 36 | 37 | ![Demo Two](https://github.com/ruddra/blog-images/raw/master/Demo%202.png) 38 | 39 | ## Commands 40 | To use this project, run this commands: 41 | 42 | 1. `make up` to build the project and starting containers. 43 | 2. `make build` to build the project. 44 | 3. `make start` to start containers if project has been up already. 45 | 4. `make stop` to stop containers. 46 | 5. `make shell-web` to shell access web container. 47 | 6. `make shell-db` to shell access db container. 48 | 7. `make shell-nginx` to shell access nginx container. 49 | 8. `make logs-web` to log access web container. 50 | 9. `make logs-db` to log access db container. 51 | 10. `make logs-nginx` to log access nginx container. 52 | 11. `make collectstatic` to put static files in static directory. 53 | 12. `make log-web` to log access web container. 54 | 13. `make log-db` to log access db container. 55 | 14. `make log-nginx` to log access nginx container. 56 | 15. `make restart` to restart containers. 57 | 58 | ## License 59 | [MIT](https://github.com/ruddra/docker-django/blob/master/LICENSE). 60 | 61 | ## Contribute 62 | 63 | Feel free to fork and create PR. 64 | -------------------------------------------------------------------------------- /src/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', 'mydjango.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /src/myapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruddra/docker-django/5aac86036a8548555e683d0247a0d7315e948314/src/myapp/__init__.py -------------------------------------------------------------------------------- /src/myapp/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import DemoModel 3 | 4 | admin.site.register(DemoModel) 5 | -------------------------------------------------------------------------------- /src/myapp/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MyappConfig(AppConfig): 5 | name = 'myapp' 6 | -------------------------------------------------------------------------------- /src/myapp/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import ItemBatch 3 | 4 | 5 | class SomeForm(forms.ModelForm): 6 | class Meta: 7 | model = ItemBatch 8 | fields = '__all__' 9 | 10 | def save(self, commit=True): 11 | image = self.cleaned_data.pop('image') 12 | instance = super().save(commit=True) 13 | # instance.save_m2m() 14 | print(image) 15 | instance.image = image 16 | instance.save() 17 | return instance 18 | -------------------------------------------------------------------------------- /src/myapp/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.1 on 2019-10-23 12:34 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='DemoModel', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('title', models.CharField(max_length=255)), 19 | ('body', models.TextField()), 20 | ('image', models.ImageField(upload_to='demo_images')), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /src/myapp/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruddra/docker-django/5aac86036a8548555e683d0247a0d7315e948314/src/myapp/migrations/__init__.py -------------------------------------------------------------------------------- /src/myapp/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class DemoModel(models.Model): 5 | title = models.CharField(max_length=255) 6 | body = models.TextField() 7 | image = models.ImageField(upload_to="demo_images") 8 | 9 | def __str__(self): 10 | return self.title 11 | -------------------------------------------------------------------------------- /src/myapp/tasks.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | import logging 3 | 4 | from django.conf import settings 5 | from mydjango.celery import app 6 | 7 | logger = logging.getLogger("celery") 8 | 9 | 10 | @app.task 11 | def show_hello_world(): 12 | logger.info("-"*25) 13 | logger.info("Printing Hello from Celery") 14 | logger.info("-"*25) 15 | -------------------------------------------------------------------------------- /src/myapp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /src/myapp/views.py: -------------------------------------------------------------------------------- 1 | from django import get_version 2 | from django.views.generic import TemplateView 3 | from .tasks import show_hello_world 4 | from .models import DemoModel 5 | # Create your views here. 6 | 7 | 8 | class ShowHelloWorld(TemplateView): 9 | template_name = 'hello_world.html' 10 | 11 | def get(self, *args, **kwargs): 12 | show_hello_world.apply() 13 | return super().get(*args, **kwargs) 14 | 15 | def get_context_data(self, **kwargs): 16 | context = super().get_context_data(**kwargs) 17 | context['demo_content'] = DemoModel.objects.all() 18 | context['version'] = get_version() 19 | return context 20 | -------------------------------------------------------------------------------- /src/mydjango/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruddra/docker-django/5aac86036a8548555e683d0247a0d7315e948314/src/mydjango/__init__.py -------------------------------------------------------------------------------- /src/mydjango/celery.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | import os 3 | from celery import Celery 4 | import logging 5 | logger = logging.getLogger("Celery") 6 | 7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mydjango.settings') 8 | 9 | app = Celery('mydjango') 10 | 11 | app.config_from_object('django.conf:settings', namespace='CELERY') 12 | 13 | app.autodiscover_tasks() 14 | 15 | 16 | @app.task(bind=True) 17 | def debug_task(self): 18 | print('Request: {0!r}'.format(self.request)) 19 | 20 | -------------------------------------------------------------------------------- /src/mydjango/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for mydjango project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 's_-aro!sw@)bob$tojdq!s61$+3s22y=dbe!b5y3!p4ch&y3k#' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['web'] 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 | 'myapp.apps.MyappConfig', 41 | 'celery', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | ] 53 | 54 | ROOT_URLCONF = 'mydjango.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': ['./templates'], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'mydjango.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 81 | 'NAME': os.environ.get("DB_NAME"), 82 | 'USER': os.environ.get("DB_USER"), 83 | 'PASSWORD': os.environ.get("DB_PASSWORD"), 84 | 'HOST': os.environ.get("DB_HOST"), 85 | 'PORT': os.environ.get("DB_PORT"), 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'en-us' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | MEDIA_URL = '/media/' 128 | 129 | CELERY_BROKER_URL = 'redis://redis:6379/0' 130 | CELERY_RESULT_BACKEND = 'redis://redis:6379/0' 131 | 132 | STATIC_ROOT = './static/' 133 | MEDIA_ROOT = './media/' 134 | 135 | LOGGING = { 136 | 'version': 1, 137 | 'disable_existing_loggers': False, 138 | 'handlers': { 139 | 'console': { 140 | 'class': 'logging.StreamHandler', 141 | }, 142 | }, 143 | 'loggers': { 144 | 'django': { 145 | 'handlers': ['console'], 146 | 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 147 | }, 148 | 'celery': { 149 | 'handlers': ['console'], 150 | 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 151 | }, 152 | }, 153 | } 154 | 155 | # ---------------- Local Settings --------------------------------------- 156 | # Put your local settings in mydjango directory to override this settings 157 | # File name should be local_settings.py 158 | try: 159 | from .local_settings import * 160 | except ImportError: 161 | print('No Local Settings Found') 162 | 163 | # ---------------- End Local Settings ------------------------------------ 164 | -------------------------------------------------------------------------------- /src/mydjango/urls.py: -------------------------------------------------------------------------------- 1 | """mydjango URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/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 import url 17 | from django.contrib import admin 18 | from django.conf.urls.static import static 19 | from django.conf import settings 20 | 21 | from myapp.views import ShowHelloWorld 22 | 23 | 24 | urlpatterns = [ 25 | url(r'^admin/', admin.site.urls), 26 | url(r'^$', ShowHelloWorld.as_view()) 27 | ] 28 | # urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 29 | # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 30 | -------------------------------------------------------------------------------- /src/mydjango/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mydjango 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.1/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', 'mydjango.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /src/requirements.pip: -------------------------------------------------------------------------------- 1 | amqp==5.0.2 2 | billiard==3.6.3.0 3 | celery==5.0.5 4 | Django==3.1.8 5 | gunicorn==20.0.4 6 | kombu==5.0.2 7 | pillow==8.1.0 8 | psycopg2==2.8.6 9 | pytz==2020.5 10 | redis==3.5.3 11 | sqlparse==0.4.1 12 | vine==5.0.0 13 | importlib-metadata==4.13.0 14 | -------------------------------------------------------------------------------- /src/templates/hello_world.html: -------------------------------------------------------------------------------- 1 | 2 | My Django Project 3 | 4 | 5 | 6 | 7 |
8 |
9 |

Hello World!!

10 |
11 |

This Project is developed using 12 | Django {{ version }}. 13 |

14 |

This page is being served through Gunicorn. You can access the 15 | admin site as well.

16 | 17 |

Static contents are being served through Nginx.

18 |

Postgres is used as database for this Project.

19 |

Please check django logs in shell using command 20 | make shell-web.

21 |

There you will see 22 | Printing Hello from Celery, which is printed from celery method.

23 |

Redis was used as broker for Celery.

24 | 25 |

Enjoy your boilerplate for Django(with celery, postgresql and redis) and Docker.

26 | {% if demo_content.exists %} 27 |

Here are some demo contents from Demo model

28 | {% for demo in demo_content %} 29 |

{{ demo.title }}

30 |

{{ demo.body }}

31 | 32 | {% endfor %} 33 | {% endif %} 34 | 35 | 36 |
37 | 38 | 39 | --------------------------------------------------------------------------------