Skip to content

Instantly share code, notes, and snippets.

@romanitalian
Created June 19, 2021 18:34
Show Gist options
  • Save romanitalian/9aca866c593a6da74d20223bb290a293 to your computer and use it in GitHub Desktop.
Save romanitalian/9aca866c593a6da74d20223bb290a293 to your computer and use it in GitHub Desktop.
make Django project from scratch (in one command).
#!/bin/bash
# README
# --------------------
# chmod u+x django_init.sh
# ./django_init.sh my-super-project.com main
# --------------------
SETTINGS_APP="core"
# cat.com
PROJECT_NAME="$1"
# main
MAIN_APP=$2
BACKEND_CONTAINER_NAME="backend"
BACKEND_CONTAINER_PORT="8000"
NGINX_CONTAINER_NAME="nginx"
NGINX_CONTAINER_PORT="80"
NGINX_PORT="8080"
if [ -z "$PROJECT_NAME" ]
then
echo "\$PROJECT_NAME is empty. Run this: $0 my-super-project.com main"
exit 1
fi
if [ -z "$MAIN_APP" ]
then
echo "\$MAIN_APP is empty. Run this: $0 my-super-project.com main"
exit 1
fi
show_msg()
{
printf "=%.0s" {1..30}
printf "\n$1\n"
printf "=%.0s" {1..30}
printf "\n"
}
git_init()
{
cat <<EOF > .gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
.idea
EOF
show_msg "git: init"
git init
git branch -m main
show_msg "git: commit all"
git add .
git commit -m 'init commit'
}
rm -rf $PROJECT_NAME
mkdir $PROJECT_NAME && cd $PROJECT_NAME
show_msg "create project: $PROJECT_NAME"
virtualenv venv
source venv/bin/activate
show_msg "install django"
pip install django
mkdir src && cd src
show_msg "create main app: $SETTINGS_APP"
django-admin startproject $SETTINGS_APP .
show_msg "create app: $MAIN_APP"
django-admin startapp $MAIN_APP
show_msg "install dependencies"
###### desired dependencies
pip install django_extensions
pip install django-debug-toolbar
pip install flake8
pip install requests
pip install celery
###### Optional dependencies
pip install psycopg2-binary
pip install redis
pip install djangorestframework
pip install django-filter
pip install drf-yasg
pip install gunicorn
pip install python-memcached
show_msg "freeze"
cd ..
pip freeze > requirements.txt
show_msg "prepare settings"
# INSTALLED_APPS
_apps=(
'django.contrib.staticfiles'
'django_extensions'
'main'
)
apps=$(printf ' '"'"'%s'"'"',\\n' "${_apps[@]}")
apps=${apps:1}
sed -i -e "s/ 'django.contrib.staticfiles',/ $apps/g" "src/$SETTINGS_APP/settings.py"
show_msg "create necessary files (templates, configs, Makefile, Dockerfile etc)"
mkdir -p "src/$MAIN_APP/templates/$MAIN_APP"
touch "src/$MAIN_APP/templates/$MAIN_APP/base.html"
touch "src/$MAIN_APP/templates/$MAIN_APP/index.html"
cat <<EOF > src/$MAIN_APP/templates/$MAIN_APP/base.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home page</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
EOF
cat <<EOF > src/$MAIN_APP/templates/$MAIN_APP/index.html
{% extends 'main/base.html' %}
{% block content %}
<h1>Hello from Django App</h1>
{% endblock %}
EOF
cat <<EOF > src/$SETTINGS_APP/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('main.urls')),
]
EOF
cat <<EOF > src/$MAIN_APP/urls.py
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
# path('', views.index, name='home_page'),
path('', TemplateView.as_view(template_name='main/index.html'), name='home_page'),
]
EOF
touch .env
cat <<EOF > .flake8
[flake8]
max-line-length=120
inline-quotes="
max-complexity=10
import-order-style=google
ignore=Q000,D103,D100,A003,D107,D105,D101,D106, D104
EOF
cat <<EOF > Makefile
include .env
export $(shell sed 's/=.*//' .env)
MANAGE=python src/manage.py
PROJECT_DIR=\$(shell pwd)
WSGI_PORT=$BACKEND_CONTAINER_PORT
RUN_COMMAND=run
BACKEND_CONTAINER=$BACKEND_CONTAINER_NAME
NGINX_CONTAINER=$NGINX_CONTAINER_NAME
help : Makefile
@echo "+--------------------+"
@echo "| AVAILABLE COMMANDS |"
@echo "+--------------------+\n"
@echo "REGULAR\n"
@cat Makefile | grep "##" | grep -v "docker" | sed -n 's/^## /make /p' | column -t -s ':' && echo ""
@echo "DOCKER\n"
@cat Makefile | grep "##" | grep "docker" | sed -n 's/^## /make /p' | column -t -s ':' && echo ""
## run: run local server.
run:
\$(MANAGE) runserver 0.0.0.0:\$(WSGI_PORT)
## run-now: run local server and open it in browser.
run-now:
make run&
open "http://127.0.0.1:\$(WSGI_PORT)"
## gunicorn-run: run gunicorn server.
gunicorn-run:
gunicorn -w 4 -b 0.0.0.0:\$(WSGI_PORT) --chdir \$(PROJECT_DIR)/src $SETTINGS_APP.wsgi --timeout 60 --log-level debug --max-requests 10000
## new-migrate: create new migration files by changes in models.
new-migrate:
\$(MANAGE) makemigrations
## migrate: apply migrations to storage (DB).
migrate:
\$(MANAGE) migrate
## check: check Django project.
check:
\$(MANAGE) check
## check-migrate: check Django migrations.
check-migrate:
\$(MANAGE) makemigrations --check --dry-run
## lint: do static code analyse.
lint:
flake8 .
## shell: open Django shell with autoloading of the apps database models and subclasses of user-defined classes.
shell:
\$(MANAGE) shell_plus --print-sql
## createsuperuser: open django prompt to add new super user.
createsuperuser:
\$(MANAGE) createsuperuser
## collect-static: store static in server.
collect-static:
\$(MANAGE) collectstatic
## freeze: freeze project dependencies into file - requirements.txt.
freeze:
pip freeze > requirements.txt
## docker-run: run app with Django dev server (runserver).
docker-run: docker-down
\$(eval RUN_COMMAND=run)
docker-compose up -d --build
#make docker-copy-static
## docker-run-gunicorn: run gunicorn server.
docker-run-gunicorn: docker-down
\$(eval RUN_COMMAND=gunicorn-run)
docker-compose up -d --build
## docker-down: stop containers.
docker-down:
docker-compose down
## docker-new-migrate: create migration files from models (in docker container).
docker-new-migrate:
docker exec -it \$(BACKEND_CONTAINER) \$(MANAGE) makemigrations
## docker-migrate: apply migrations to DB (in docker container).
docker-migrate:
docker exec -it \$(BACKEND_CONTAINER) \$(MANAGE) migrate
## docker-copy-static: copy static files from BACKEND container to NGINX container.
docker-copy-static:
docker exec -it \$(BACKEND_CONTAINER) \$(MANAGE) collectstatic --noinput
docker cp \$(BACKEND_CONTAINER):/tmp/static_content/static /tmp/static
docker cp /tmp/static \$(NGINX_CONTAINER):/etc/nginx
rm -rf /tmp/static
EOF
cat <<EOF > Dockerfile
FROM python:3.9 AS builder
RUN uname -a
RUN apt update && apt install -y --no-install-recommends python-dev python-setuptools
WORKDIR /srv/project
# This prevents Python from writing out pyc files
ENV PYTHONDONTWRITEBYTECODE 1
# This keeps Python from buffering stdin/stdout
ENV PYTHONUNBUFFERED 1
COPY requirements.txt requirements.txt
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
FROM builder AS builder_app
COPY src/ src/
COPY ./Makefile Makefile
RUN useradd -ms /bin/bash admin
RUN chown -R admin:admin /srv/project
RUN chmod 755 /srv/project
USER admin
#EXPOSE 8000
#CMD ["python", "./src/manage.py", "runserver", "0.0.0.0:8000"]
#CMD bash -C "./commands/wsgi_dev.sh"
EOF
cat <<EOF > docker-compose.yaml
version: "3.8"
services:
nginx:
build: ./nginx
container_name: nginx
ports:
- "$NGINX_PORT:$NGINX_CONTAINER_PORT"
- "443:443"
depends_on:
- backend
backend:
build: .
container_name: backend
command: make \${RUN_COMMAND}
tty: true
stdin_open: true
restart: always
ports:
- "$BACKEND_CONTAINER_PORT:$BACKEND_CONTAINER_PORT"
- "9000:9000"
volumes:
- ./:/srv/project
# depends_on:
# - memcached
# - mq
# - postgres
env_file: .env
# celery:
# build: .
# container_name: celery
# command: make celery-run
# restart: always
# env_file: .env
# depends_on:
# - mq
# volumes:
# - ./:/srv/project
#
# celerybeat:
# build: .
# container_name: celerybeat
# command: make celerybeat-run
# restart: always
# env_file: .env
# depends_on:
# - mq
# volumes:
# - ./:/srv/project
#
# memcached:
# image: memcached:1.6.9
# container_name: memcached
# tty: true
# stdin_open: true
# restart: always
# rabbitmq:
# image: rabbitmq:3.8-rc-management
# container_name: rabbitmq
# tty: true
# stdin_open: true
# restart: always
# env_file: .env
## ports:
## - "15672:15672"
# mq:
# image: redis:6.2.4
# tty: true
# stdin_open: true
# restart: always
# env_file: .env
#
# redis-commander:
# container_name: redis-commander
# hostname: redis-commander
# image: rediscommander/redis-commander:latest
# restart: always
# environment:
# - REDIS_HOSTS=redis_container:mq:6379
# ports:
# - "8081:8081"
#
# postgres:
# image: postgres:12-alpine
# tty: true
# stdin_open: true
# restart: always
# env_file: .env
# ports:
# - "5432:5432"
# volumes:
# - db_data:/var/lib/postgresql/data
#
#volumes:
# db_data:
EOF
mkdir nginx
cat <<EOF > nginx/Dockerfile
FROM nginx:1.19.0
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
COPY proxy_params /etc/nginx/conf.d/proxy_params
EOF
cat <<EOF > nginx/nginx.conf
upstream django {
server $BACKEND_CONTAINER_NAME:$BACKEND_CONTAINER_PORT;
}
server {
listen $NGINX_CONTAINER_PORT;
listen [::]:$NGINX_CONTAINER_PORT;
server_name _;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /etc/nginx;
}
location / {
include /etc/nginx/conf.d/proxy_params;
proxy_pass http://django;
}
}
EOF
cat <<EOF > nginx/proxy_params
proxy_redirect off;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
EOF
git_init
RUN_MSG="cd $PROJECT_NAME && source ./venv/bin/activate && make run"
show_msg "Project successfull created.\nNow run your project\n\n******** RUN IT NOW ********\n\n$RUN_MSG"
# just show help (list of available commands).
make
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment