diff --git a/.pyup.yml b/.pyup.yml
index a215e841e..6b4a9d8b3 100644
--- a/.pyup.yml
+++ b/.pyup.yml
@@ -14,5 +14,5 @@ requirements:
- "requirements.txt"
- "{{cookiecutter.project_slug}}/requirements/base.txt"
- "{{cookiecutter.project_slug}}/requirements/dev.txt"
- - "{{cookiecutter.project_slug}}/requirements/production.txt"
+ - "{{cookiecutter.project_slug}}/requirements/prod.txt"
- "{{cookiecutter.project_slug}}/requirements/test.txt"
diff --git a/cookiecutter.json b/cookiecutter.json
index 0a060e03e..4abbc926f 100644
--- a/cookiecutter.json
+++ b/cookiecutter.json
@@ -5,7 +5,6 @@
"author_name": "Your Name",
"domain_name": "example.com",
"email": "{{ cookiecutter.author_name.lower()|replace(' ', '-') }}@example.com",
- "version": "0.1.0",
"open_source_license": [
"MIT",
"BSD",
diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py
index 968c0f73e..1fdd779ae 100644
--- a/hooks/post_gen_project.py
+++ b/hooks/post_gen_project.py
@@ -18,6 +18,52 @@ HINT = "\x1b[3;33m"
SUCCESS = "\x1b[1;32m [SUCCESS]: "
DEBUG_VALUE = "debug"
+PROJECT_DIRECTORY = os.path.realpath(os.path.curdir)
+
+
+def initialize_git(project_directory):
+ """
+ Initialize the git repo.
+
+ Args:
+ project_directory:
+ """
+ import subprocess
+
+ print("Initializing git repo...")
+ result = subprocess.run(
+ ["git", "init"], cwd=project_directory, encoding="utf8", capture_output=True
+ )
+ if result.returncode != 0:
+ print("Unable to initialize the git repo.")
+ print(result.stdout, result.stderr)
+
+ result = subprocess.run(
+ ["git", "add", "."], cwd=project_directory, encoding="utf8", capture_output=True
+ )
+ if result.returncode != 0:
+ print("Unable to add all files into the git repo.")
+ print(result.stdout, result.stderr)
+
+ result = subprocess.run(
+ ["git", "commit", '-m"Initial commit"'],
+ cwd=project_directory,
+ encoding="utf8",
+ capture_output=True,
+ )
+ if result.returncode != 0:
+ print("Unable to make the initial commit.")
+ print(result.stdout, result.stderr)
+
+ result = subprocess.run(
+ ["git", "tag", "0.1.0"],
+ cwd=project_directory,
+ encoding="utf8",
+ capture_output=True,
+ )
+ if result.returncode != 0:
+ print("Unable to tag the initial commit.")
+ print(result.stdout, result.stderr)
def remove_open_source_files():
@@ -32,20 +78,10 @@ def remove_gplv3_files():
os.remove(file_name)
-def remove_pycharm_files():
- idea_dir_path = ".idea"
- if os.path.exists(idea_dir_path):
- shutil.rmtree(idea_dir_path)
-
- docs_dir_path = os.path.join("docs", "pycharm")
- if os.path.exists(docs_dir_path):
- shutil.rmtree(docs_dir_path)
-
-
def remove_docker_files():
shutil.rmtree("compose")
- file_names = ["dev.yml", ".dockerignore"]
+ file_names = ["local.yml", ".dockerignore"]
for file_name in file_names:
os.remove(file_name)
@@ -146,20 +182,8 @@ def set_django_secret_key(file_path):
return django_secret_key
-def set_django_admin_url(file_path):
- django_admin_url = set_flag(
- file_path,
- "!!!SET DJANGO_ADMIN_URL!!!",
- formatted="{}/",
- length=32,
- using_digits=True,
- using_ascii_letters=True,
- )
- return django_admin_url
-
-
def generate_random_user():
- return generate_random_string(length=32, using_ascii_letters=True)
+ return generate_random_string(length=10, using_ascii_letters=True)
def generate_postgres_user(debug=False):
@@ -209,36 +233,23 @@ def append_to_gitignore_file(s):
def set_flags_in_envs(postgres_user, celery_flower_user, debug=False):
- local_django_envs_path = os.path.join(".envs", "dev", "django")
production_django_envs_path = os.path.join(".envs", "prod", "django")
- local_postgres_envs_path = os.path.join(".envs", "dev", "postgres")
-
+ dev_postgres_env_path = os.path.join(".envs", "dev", "postgres")
+ pg_pass = set_postgres_password(dev_postgres_env_path)
+ set_flag(production_django_envs_path, "!!!SET POSTGRES_PASSWORD!!!", value=pg_pass)
set_django_secret_key(production_django_envs_path)
- set_django_admin_url(production_django_envs_path)
-
- set_postgres_user(local_postgres_envs_path, value=postgres_user)
- set_postgres_password(
- local_postgres_envs_path, value=DEBUG_VALUE if debug else None
- )
-
- set_celery_flower_user(local_django_envs_path, value=celery_flower_user)
- set_celery_flower_password(
- local_django_envs_path, value=DEBUG_VALUE if debug else None
- )
set_celery_flower_user(production_django_envs_path, value=celery_flower_user)
- set_celery_flower_password(
- production_django_envs_path, value=DEBUG_VALUE if debug else None
- )
+ set_celery_flower_password(production_django_envs_path)
def set_flags_in_settings_files():
- set_django_secret_key(
- os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev_template.py")
- )
set_django_secret_key(
os.path.join("{{ cookiecutter.project_slug }}", "settings", "test.py")
)
+def remove_storage():
+ os.remove(os.path.join("{{ cookiecutter.project_slug }}", "storage.py"))
+
def remove_envs_and_associated_files():
shutil.rmtree(".envs")
@@ -250,6 +261,12 @@ def create_dev_settings():
os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev_template.py"),
os.path.join("{{ cookiecutter.project_slug }}", "settings", "dev.py"),
)
+ shutil.copy(os.path.join(".envs", "prod", "django"), ".env")
+ set_flag(
+ ".env",
+ "DJANGO_SETTINGS_MODULE=test_project.settings.prod",
+ value="DJANGO_SETTINGS_MODULE=test_project.settings",
+ )
def main():
@@ -263,14 +280,14 @@ def main():
if "{{ cookiecutter.open_source_license}}" != "GPLv3":
remove_gplv3_files()
- append_to_gitignore_file(".env")
- append_to_gitignore_file(".envs/*")
-
if "{{ cookiecutter.cloud_provider}}".lower() == "none":
print(
WARNING + "You chose not to use a cloud provider, "
"media files won't be served in production." + TERMINATOR
)
+ remove_storage()
+ elif "{{ cookiecutter.cloud_provider}}".lower() == "GCP":
+ remove_storage()
if "{{ cookiecutter.use_celery }}".lower() == "n":
remove_celery_files()
@@ -278,6 +295,8 @@ def main():
if "{{ cookiecutter.use_travisci }}".lower() == "n":
remove_dottravisyml_file()
+ initialize_git(PROJECT_DIRECTORY)
+
print(SUCCESS + "Project initialized, keep up the good work!" + TERMINATOR)
diff --git a/{{cookiecutter.project_slug}}/.envs/dev/django b/{{cookiecutter.project_slug}}/.envs/dev/django
deleted file mode 100644
index 919f31185..000000000
--- a/{{cookiecutter.project_slug}}/.envs/dev/django
+++ /dev/null
@@ -1,17 +0,0 @@
-# General
-# ------------------------------------------------------------------------------
-USE_DOCKER=yes
-IPYTHONDIR=/app/.ipython
-
-{%- if cookiecutter.use_celery == 'y' %}
-# Redis
-# ------------------------------------------------------------------------------
-REDIS_URL=redis://redis:6379/0
-
-# Celery
-# ------------------------------------------------------------------------------
-
-# Flower
-CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
-CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!
-{% endif %}
diff --git a/{{cookiecutter.project_slug}}/.envs/dev/postgres b/{{cookiecutter.project_slug}}/.envs/dev/postgres
index f190db8e6..eced92192 100644
--- a/{{cookiecutter.project_slug}}/.envs/dev/postgres
+++ b/{{cookiecutter.project_slug}}/.envs/dev/postgres
@@ -1,7 +1,6 @@
-# PostgreSQL
-# ------------------------------------------------------------------------------
-POSTGRES_HOST=postgres
-POSTGRES_PORT=5432
-POSTGRES_DB={{ cookiecutter.project_slug }}
-POSTGRES_USER=!!!SET POSTGRES_USER!!!
POSTGRES_PASSWORD=!!!SET POSTGRES_PASSWORD!!!
+# PGDATA=/var/lib/postgresql/data
+# POSTGRES_INITDB_WALDIR=
+# POSTGRES_INITDB_ARGS=
+# POSTGRES_USER=postgres
+# POSTGRES_DB=postgres
diff --git a/{{cookiecutter.project_slug}}/.envs/prod/django b/{{cookiecutter.project_slug}}/.envs/prod/django
index 2c2e94f2d..895ab6cb3 100644
--- a/{{cookiecutter.project_slug}}/.envs/prod/django
+++ b/{{cookiecutter.project_slug}}/.envs/prod/django
@@ -1,32 +1,47 @@
# General
# ------------------------------------------------------------------------------
-# DJANGO_READ_DOT_ENV_FILE=True
-DJANGO_SETTINGS_MODULE=config.settings.production
+DJANGO_SETTINGS_MODULE={{ cookiecutter.project_slug }}.settings.prod
DJANGO_SECRET_KEY=!!!SET DJANGO_SECRET_KEY!!!
-DJANGO_ADMIN_URL=!!!SET DJANGO_ADMIN_URL!!!
+DJANGO_ADMIN_URL=admin/
DJANGO_ALLOWED_HOSTS=.{{ cookiecutter.domain_name }}
+DJANGO_DEBUG=False
+
+DATABASE_URL=postgres://postgres:!!!SET POSTGRES_PASSWORD!!!@localhost/{{ cookiecutter.project_slug }}
# Security
# ------------------------------------------------------------------------------
# TIP: better off using DNS, however, redirect is OK too
DJANGO_SECURE_SSL_REDIRECT=False
+DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True
+DJANGO_SECURE_HSTS_PRELOAD=True
+DJANGO_SECURE_CONTENT_TYPE_NOSNIFF=True
# Email
# ------------------------------------------------------------------------------
-MAILGUN_API_KEY=
DJANGO_SERVER_EMAIL=
-MAILGUN_DOMAIN=
+DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
+EMAIL_HOST=localhost
+EMAIL_HOST_USER=
+EMAIL_HOST_PASSWORD=
+EMAIL_PORT=25
+DJANGO_EMAIL_SUBJECT_PREFIX="[{{cookiecutter.project_name}}]"
+
{% if cookiecutter.cloud_provider == 'AWS' %}
# AWS
# ------------------------------------------------------------------------------
DJANGO_AWS_ACCESS_KEY_ID=
DJANGO_AWS_SECRET_ACCESS_KEY=
DJANGO_AWS_STORAGE_BUCKET_NAME=
+DJANGO_AWS_S3_REGION_NAME=
{% elif cookiecutter.cloud_provider == 'GCP' %}
# GCP
# ------------------------------------------------------------------------------
GOOGLE_APPLICATION_CREDENTIALS=
DJANGO_GCP_STORAGE_BUCKET_NAME=
+{% elif cookiecutter.cloud_provider == 'Azure' %}
+DJANGO_AZURE_ACCOUNT_NAME=
+DJANGO_AZURE_ACCOUNT_KEY=
+DJANGO_AZURE_CONTAINER=
{% endif %}
# django-allauth
# ------------------------------------------------------------------------------
@@ -43,15 +58,16 @@ WEB_CONCURRENCY=4
# Sentry
# ------------------------------------------------------------------------------
SENTRY_DSN=
+# SENTRY_LOG_LEVEL=
{% endif %}
# Redis
# ------------------------------------------------------------------------------
-REDIS_URL=redis://redis:6379/0
+REDIS_URL=redis://localhost:6379/0
{% if cookiecutter.use_celery == 'y' %}
# Celery
# ------------------------------------------------------------------------------
-
+CELERY_BROKER_URL=redis://localhost:6379/0
# Flower
CELERY_FLOWER_USER=!!!SET CELERY_FLOWER_USER!!!
CELERY_FLOWER_PASSWORD=!!!SET CELERY_FLOWER_PASSWORD!!!
diff --git a/{{cookiecutter.project_slug}}/.idea/runConfigurations/merge_production_dotenvs_in_dotenv.xml b/{{cookiecutter.project_slug}}/.idea/runConfigurations/merge_production_dotenvs_in_dotenv.xml
index cf2c5dd89..293eafe90 100644
--- a/{{cookiecutter.project_slug}}/.idea/runConfigurations/merge_production_dotenvs_in_dotenv.xml
+++ b/{{cookiecutter.project_slug}}/.idea/runConfigurations/merge_production_dotenvs_in_dotenv.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/{{cookiecutter.project_slug}}/.idea/runConfigurations/migrate.xml b/{{cookiecutter.project_slug}}/.idea/runConfigurations/migrate.xml
index b457c5f55..8306ddc33 100644
--- a/{{cookiecutter.project_slug}}/.idea/runConfigurations/migrate.xml
+++ b/{{cookiecutter.project_slug}}/.idea/runConfigurations/migrate.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver.xml b/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver.xml
index 2a348a2e7..8814ca594 100644
--- a/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver.xml
+++ b/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver.xml
@@ -1,17 +1,17 @@
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver_plus.xml b/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver_plus.xml
index 242f861a6..5c893f0a9 100644
--- a/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver_plus.xml
+++ b/{{cookiecutter.project_slug}}/.idea/runConfigurations/runserver_plus.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/Dockerfile
index 441e687ca..627ca2407 100644
--- a/{{cookiecutter.project_slug}}/Dockerfile
+++ b/{{cookiecutter.project_slug}}/Dockerfile
@@ -25,41 +25,15 @@ RUN addgroup -S django \
# Requirements are installed here to ensure they will be cached.
COPY ./requirements /requirements
-RUN pip install --no-cache-dir -r /requirements/production.txt \
+RUN pip install --no-cache-dir -r /requirements/prod.txt \
&& rm -rf /requirements
-COPY ./compose/production/django/entrypoint /entrypoint
-RUN sed -i 's/\r$//g' /entrypoint
-RUN chmod +x /entrypoint
-RUN chown django /entrypoint
-
-COPY ./compose/production/django/start /start
-RUN sed -i 's/\r$//g' /start
-RUN chmod +x /start
-RUN chown django /start
-
-{%- if cookiecutter.use_celery == "y" %}
-COPY ./compose/production/django/celery/worker/start /start-celeryworker
-RUN sed -i 's/\r$//g' /start-celeryworker
-RUN chmod +x /start-celeryworker
-RUN chown django /start-celeryworker
-
-COPY ./compose/production/django/celery/beat/start /start-celerybeat
-RUN sed -i 's/\r$//g' /start-celerybeat
-RUN chmod +x /start-celerybeat
-RUN chown django /start-celerybeat
-
-COPY ./compose/production/django/celery/flower/start /start-flower
-RUN sed -i 's/\r$//g' /start-flower
-RUN chmod +x /start-flower
-{%- endif %}
-
COPY --from=client-builder /app /app
RUN chown -R django /app
-
+RUN chmod +x /app/bin/*
USER django
WORKDIR /app
-ENTRYPOINT ["/entrypoint"]
+ENTRYPOINT ["/app/bin/entrypoint"]
diff --git a/{{cookiecutter.project_slug}}/README.rst b/{{cookiecutter.project_slug}}/README.rst
index 5bd309aea..8342b3969 100644
--- a/{{cookiecutter.project_slug}}/README.rst
+++ b/{{cookiecutter.project_slug}}/README.rst
@@ -79,7 +79,7 @@ To run a celery worker:
.. code-block:: bash
cd {{cookiecutter.project_slug}}
- celery -A config.celery_app worker -l info
+ celery -A {{ cookiecutter.project_slug }}.celery_app worker -l info
Please note: For Celery's import magic to work, it is important *where* the celery commands are run. If you are in the same folder with *manage.py*, you should be right.
diff --git a/{{cookiecutter.project_slug}}/bin/entrypoint b/{{cookiecutter.project_slug}}/bin/entrypoint
old mode 100644
new mode 100755
index 0a76b3107..17d4a7e05
--- a/{{cookiecutter.project_slug}}/bin/entrypoint
+++ b/{{cookiecutter.project_slug}}/bin/entrypoint
@@ -10,12 +10,13 @@ set -o nounset
export CELERY_BROKER_URL="${REDIS_URL}"
{% endif %}
-if [ -z "${POSTGRES_USER}" ]; then
- base_postgres_image_default_user='postgres'
- export POSTGRES_USER="${base_postgres_image_default_user}"
+if [ -z "${DATABASE_URL}" ]; then
+ if [ -z "${POSTGRES_USER}" ]; then
+ base_postgres_image_default_user='postgres'
+ export POSTGRES_USER="${base_postgres_image_default_user}"
+ fi
+ export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
fi
-export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
-
postgres_ready() {
python << END
import sys
@@ -23,13 +24,7 @@ import sys
import psycopg2
try:
- psycopg2.connect(
- dbname="${POSTGRES_DB}",
- user="${POSTGRES_USER}",
- password="${POSTGRES_PASSWORD}",
- host="${POSTGRES_HOST}",
- port="${POSTGRES_PORT}",
- )
+ psycopg2.connect("${DATABASE_URL}")
except psycopg2.OperationalError:
sys.exit(-1)
sys.exit(0)
diff --git a/{{cookiecutter.project_slug}}/bin/merge_production_dotenvs_in_dotenv.py b/{{cookiecutter.project_slug}}/bin/merge_production_dotenvs_in_dotenv.py
old mode 100644
new mode 100755
diff --git a/{{cookiecutter.project_slug}}/bin/setup-dev-env b/{{cookiecutter.project_slug}}/bin/setup-dev-env
new file mode 100755
index 000000000..e4712dfcf
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/bin/setup-dev-env
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+PROJECT_PATH=`pwd`
+ENV_PATH="${PROJECT_PATH}/.envs/prod/django"
+SETTINGS_PATH="${PROJECT_PATH}/{{ cookiecutter.project_slug }}/settings/dev_template.py"
+
+echo "Creating local .env file"
+cp ${ENV_PATH} "${PROJECT_PATH}/.env"
+
+echo "Creating local dev.py settings"
+cp ${SETTINGS_PATH} "${PROJECT_PATH}/{{ cookiecutter.project_slug }}/settings/dev.py"
diff --git a/{{cookiecutter.project_slug}}/bin/start-celery-beat b/{{cookiecutter.project_slug}}/bin/start-celery-beat
old mode 100644
new mode 100755
index 0e793e38e..f9bcc7199
--- a/{{cookiecutter.project_slug}}/bin/start-celery-beat
+++ b/{{cookiecutter.project_slug}}/bin/start-celery-beat
@@ -5,4 +5,4 @@ set -o pipefail
set -o nounset
-celery -A config.celery_app beat -l INFO
+celery -A {{ cookiecutter.project_slug }}.celery_app beat -l INFO
diff --git a/{{cookiecutter.project_slug}}/bin/start-celery-worker b/{{cookiecutter.project_slug}}/bin/start-celery-worker
old mode 100644
new mode 100755
index 4e519c3fc..4b21b6d13
--- a/{{cookiecutter.project_slug}}/bin/start-celery-worker
+++ b/{{cookiecutter.project_slug}}/bin/start-celery-worker
@@ -5,4 +5,4 @@ set -o pipefail
set -o nounset
-celery -A config.celery_app worker -l INFO
+celery -A {{ cookiecutter.project_slug }}.celery_app worker -l INFO
diff --git a/{{cookiecutter.project_slug}}/bin/start-django b/{{cookiecutter.project_slug}}/bin/start-django
old mode 100644
new mode 100755
index aa425b9d9..8c7f47aa4
--- a/{{cookiecutter.project_slug}}/bin/start-django
+++ b/{{cookiecutter.project_slug}}/bin/start-django
@@ -6,4 +6,4 @@ set -o nounset
python /app/manage.py collectstatic --noinput
-/usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:8000 --chdir=/app
+/usr/local/bin/gunicorn {{ cookiecutter.project_slug }}.wsgi --bind 0.0.0.0:8000 --chdir=/app
diff --git a/{{cookiecutter.project_slug}}/bin/start-django-dev b/{{cookiecutter.project_slug}}/bin/start-django-dev
old mode 100644
new mode 100755
diff --git a/{{cookiecutter.project_slug}}/bin/start-flower b/{{cookiecutter.project_slug}}/bin/start-flower
old mode 100644
new mode 100755
index be67050da..2d1c82d7d
--- a/{{cookiecutter.project_slug}}/bin/start-flower
+++ b/{{cookiecutter.project_slug}}/bin/start-flower
@@ -5,6 +5,6 @@ set -o nounset
celery flower \
- --app=config.celery_app \
+ --app={{ cookiecutter.project_slug }}.celery_app \
--broker="${CELERY_BROKER_URL}" \
--basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}"
diff --git a/{{cookiecutter.project_slug}}/local.yml b/{{cookiecutter.project_slug}}/local.yml
index 9b23c7ec0..3ab9dfac5 100644
--- a/{{cookiecutter.project_slug}}/local.yml
+++ b/{{cookiecutter.project_slug}}/local.yml
@@ -8,8 +8,8 @@ services:
django:{% if cookiecutter.use_celery == 'y' %} &django{% endif %}
build:
context: .
- dockerfile: ./compose/dev/django/Dockerfile
- image: {{ cookiecutter.project_slug }}_local_django
+ dockerfile: ./Dockerfile
+ image: {{ cookiecutter.project_slug }}_django
depends_on:
- postgres
{%- if cookiecutter.use_mailhog == 'y' %}
@@ -18,22 +18,20 @@ services:
volumes:
- .:/app
env_file:
- - ./.envs/dev/django
- - ./.envs/dev/postgres
+ - .env
ports:
- "8000:8000"
- command: /start
+ command: /app/bin/start-django-dev
postgres:
- build:
- context: .
- dockerfile: postgres:11-alpine
- image: {{ cookiecutter.project_slug }}_postgres
+ image: postgres:11-alpine
volumes:
- local_postgres_data:/var/lib/postgresql/data
- local_postgres_data_backups:/backups
env_file:
- - .envs/dev/postgres
+ - .env
+ ports:
+ - "5432:5432"
{%- if cookiecutter.use_mailhog == 'y' %}
mailhog:
@@ -56,7 +54,7 @@ services:
- mailhog
{%- endif %}
ports: []
- command: /start-celeryworker
+ command: /app/bin/start-celeryworker
celerybeat:
<<: *django
@@ -68,22 +66,19 @@ services:
- mailhog
{%- endif %}
ports: []
- command: /start-celerybeat
+ command: /app/bin/start-celerybeat
flower:
<<: *django
image: {{ cookiecutter.project_slug }}_flower
ports:
- "5555:5555"
- command: /start-flower
+ command: /app/bin/start-flower
{%- endif %}
node:
- build:
- context: .
- dockerfile: node:10-stretch-slim
- image: {{ cookiecutter.project_slug }}_node
+ image: node:10-stretch-slim
depends_on:
- django
volumes:
diff --git a/{{cookiecutter.project_slug}}/package.json b/{{cookiecutter.project_slug}}/package.json
index b5cd6a794..4abe12651 100644
--- a/{{cookiecutter.project_slug}}/package.json
+++ b/{{cookiecutter.project_slug}}/package.json
@@ -1,6 +1,6 @@
{
"name": "{{cookiecutter.project_slug}}",
- "version": "{{ cookiecutter.version }}",
+ "version": "1.0.0",
"dependencies": {},
"devDependencies": {
"autoprefixer": "^9.4.7",
diff --git a/{{cookiecutter.project_slug}}/pytest.ini b/{{cookiecutter.project_slug}}/pytest.ini
index efa7e432a..eda65b5e8 100644
--- a/{{cookiecutter.project_slug}}/pytest.ini
+++ b/{{cookiecutter.project_slug}}/pytest.ini
@@ -1,3 +1,3 @@
[pytest]
-addopts = --ds=config.settings.test
+addopts = --ds={{ cookiecutter.project_slug }}.settings.test
norecursedirs = node_modules
diff --git a/{{cookiecutter.project_slug}}/requirements/base.txt b/{{cookiecutter.project_slug}}/requirements/base.txt
index eb656acf8..3f5917535 100644
--- a/{{cookiecutter.project_slug}}/requirements/base.txt
+++ b/{{cookiecutter.project_slug}}/requirements/base.txt
@@ -29,3 +29,10 @@ django-redis==4.10.0 # https://github.com/niwinz/django-redis
# Django REST Framework
djangorestframework==3.10.0 # https://github.com/encode/django-rest-framework
coreapi==2.3.3 # https://github.com/core-api/python-client
+{%- if cookiecutter.cloud_provider == 'AWS' %}
+django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
+{%- elif cookiecutter.cloud_provider == 'GCP' %}
+django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
+{%- elif cookiecutter.cloud_provider == 'Azure' %}
+django-storages[azure]==1.7.1 # https://github.com/jschneier/django-storages
+{%- endif %}
diff --git a/{{cookiecutter.project_slug}}/requirements/prod.txt b/{{cookiecutter.project_slug}}/requirements/prod.txt
new file mode 100644
index 000000000..1cbc9c0e7
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/requirements/prod.txt
@@ -0,0 +1,9 @@
+# PRECAUTION: avoid production dependencies that aren't in development
+
+-r ./base.txt
+
+gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
+psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
+{%- if cookiecutter.use_sentry == "y" %}
+sentry-sdk==0.10.2 # https://github.com/getsentry/sentry-python
+{%- endif %}
diff --git a/{{cookiecutter.project_slug}}/requirements/production.txt b/{{cookiecutter.project_slug}}/requirements/production.txt
deleted file mode 100644
index ff1bbf3c2..000000000
--- a/{{cookiecutter.project_slug}}/requirements/production.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# PRECAUTION: avoid production dependencies that aren't in development
-
--r ./base.txt
-
-gunicorn==19.9.0 # https://github.com/benoitc/gunicorn
-psycopg2==2.8.3 --no-binary psycopg2 # https://github.com/psycopg/psycopg2
-{%- if cookiecutter.use_sentry == "y" %}
-sentry-sdk==0.10.2 # https://github.com/getsentry/sentry-python
-{%- endif %}
-
-# Django
-# ------------------------------------------------------------------------------
-{%- if cookiecutter.cloud_provider == 'AWS' %}
-django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages
-{%- elif cookiecutter.cloud_provider == 'GCP' %}
-django-storages[google]==1.7.1 # https://github.com/jschneier/django-storages
-{%- elif cookiecutter.cloud_provider == 'Azure' %}
-django-storages[azure]==1.7.1 # https://github.com/jschneier/django-storages
-{%- endif %}
-django-anymail[mailgun]==6.1.0 # https://github.com/anymail/django-anymail
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py
index b4056707a..c6b513e0b 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "{{ cookiecutter.version }}"
+__version__ = ""
__version_info__ = tuple(
[
int(num) if num.isdigit() else num
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/base.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/base.py
index e83c7c5e3..8be28d1fb 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/base.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/base.py
@@ -1,21 +1,6 @@
"""
Base settings to build other settings files upon.
"""
-{% if cookiecutter.use_sentry == 'y' -%}
-import logging
-
-import sentry_sdk
-
-from sentry_sdk.integrations.django import DjangoIntegration
-from sentry_sdk.integrations.logging import LoggingIntegration
-{%- if cookiecutter.use_celery == 'y' %}
-from sentry_sdk.integrations.celery import CeleryIntegration
-{% endif %}{% endif -%}
-{% if cookiecutter.cloud_provider == 'AWS' %}
-from storages.backends.s3boto3 import S3Boto3Storage
-{% elif cookiecutter.cloud_provider == 'Azure' %}
-from storages.backends.azure_storage import AzureStorage
-{%- endif %}
import environ
ROOT_DIR = (
@@ -32,7 +17,7 @@ env.read_env(str(ROOT_DIR.path(".env")))
# ------------------------------------------------------------------------------
DEBUG = env.bool("DJANGO_DEBUG", False)
SITE_ID = 1
-ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["{{ cookiecutter.domain_name }}"])
+ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=[".{{ cookiecutter.domain_name }}"])
# DATABASES
@@ -234,28 +219,13 @@ STATICFILES_FINDERS = [
{%- endif %}
]
{%- if cookiecutter.cloud_provider == 'AWS' %}
-
-
-class StaticRootS3Boto3Storage(S3Boto3Storage):
- location = "static"
- default_acl = "public-read"
-
-
-STATICFILES_STORAGE = "settings.base.StaticRootS3Boto3Storage"
+STATICFILES_STORAGE = "{{ cookiecutter.project_slug }}.storage.StaticRootS3Boto3Storage"
STATIC_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/static/"
{% elif cookiecutter.cloud_provider == 'GCP' %}
STATICFILES_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
STATIC_URL = f"https://storage.googleapis.com/{GS_BUCKET_NAME}/static/"
{% elif cookiecutter.cloud_provider == 'Azure' %}
-
-
-class PublicAzureStorage(AzureStorage):
- account_name = AZURE_ACCOUNT_NAME
- account_key = AZURE_ACCOUNT_KEY
- azure_container = AZURE_CONTAINER
- expiration_secs = None
-
-STATICFILES_STORAGE = "settings.base.PublicAzureStorage"
+STATICFILES_STORAGE = "{{ cookiecutter.project_slug }}.storage.PublicAzureStorage"
{% elif cookiecutter.use_whitenoise == 'y' %}
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
{% else %}
@@ -273,14 +243,7 @@ COMPRESS_URL = STATIC_URL
# https://docs.djangoproject.com/en/2.2/ref/settings/#file-uploads
# ------------------------------------------------------------------------------
{%- if cookiecutter.cloud_provider == 'AWS' %}
-
-
-class MediaRootS3Boto3Storage(S3Boto3Storage):
- location = "media"
- file_overwrite = False
-
-
-DEFAULT_FILE_STORAGE = "config.settings.production.MediaRootS3Boto3Storage"
+DEFAULT_FILE_STORAGE = "{{ cookiecutter.project_slug }}.storage.MediaRootS3Boto3Storage"
MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/media/"
{% elif cookiecutter.cloud_provider == 'GCP' %}
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
@@ -384,34 +347,8 @@ LOGGING = {
"handlers": ["console"],
"propagate": False,
},
-{%- if cookiecutter.use_sentry == 'y' %}
- "sentry_sdk": { # Errors logged by Sentry's SDK itself
- "level": "ERROR",
- "handlers": ["console"],
- "propagate": False,
- },
- {%- endif %}
},
}
-
-{%- if cookiecutter.use_sentry == 'y' %}
-# Sentry
-# ------------------------------------------------------------------------------
-SENTRY_DSN = env("SENTRY_DSN")
-SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
-
-sentry_logging = LoggingIntegration(
- level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
- event_level=logging.ERROR, # Send errors as events
-)
-{%- if cookiecutter.use_celery == 'y' %}
-sentry_sdk.init(
- dsn=SENTRY_DSN,
- integrations=[sentry_logging, DjangoIntegration(), CeleryIntegration()],
-)
-{%- else %}
-sentry_sdk.init(dsn=SENTRY_DSN, integrations=[sentry_logging, DjangoIntegration()])
-{% endif -%}{%- endif %}
{%- if cookiecutter.use_celery == 'y' %}
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/dev_template.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/dev_template.py
index 3eecbe374..4f678e02b 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/dev_template.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/dev_template.py
@@ -3,19 +3,11 @@ from .base import env
# GENERAL
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = True
-# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
-SECRET_KEY = env(
- "DJANGO_SECRET_KEY",
- default="!!!SET DJANGO_SECRET_KEY!!!",
-)
-# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
# CACHES
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/prod.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/prod.py
index ea1063d7c..e722ed6e2 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/prod.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/prod.py
@@ -1,5 +1,17 @@
from .base import * # noqa
+from .base import env # noqa
+{% if cookiecutter.use_sentry == 'y' -%}
+import logging
+import sentry_sdk
+
+from sentry_sdk.integrations.django import DjangoIntegration
+from sentry_sdk.integrations.logging import LoggingIntegration
+{% if cookiecutter.use_celery == 'y' -%}
+from sentry_sdk.integrations.celery import CeleryIntegration
+{% endif %}{% endif %}
+
+DEBUG = False # Forcible override to turn DEBUG off in production
# TEMPLATES
# https://docs.djangoproject.com/en/dev/ref/settings/#templates
@@ -13,3 +25,66 @@ TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
],
)
]
+
+# LOGGING
+# https://docs.djangoproject.com/en/dev/ref/settings/#logging
+# ------------------------------------------------------------------------------
+# See https://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ "version": 1,
+ "disable_existing_loggers": False,
+ "formatters": {
+ "verbose": {
+ "format": "%(levelname)s %(asctime)s %(module)s "
+ "%(process)d %(thread)d %(message)s"
+ }
+ },
+ "handlers": {
+ "console": {
+ "level": "DEBUG",
+ "class": "logging.StreamHandler",
+ "formatter": "verbose",
+ }
+ },
+ "root": {"level": "INFO", "handlers": ["console"]},
+ "loggers": {
+ "django.db.backends": {
+ "level": "ERROR",
+ "handlers": ["console"],
+ "propagate": False,
+ },
+ "django.security.DisallowedHost": {
+ "level": "ERROR",
+ "handlers": ["console"],
+ "propagate": False,
+ },
+ {%- if cookiecutter.use_sentry == 'y' %}
+ "sentry_sdk": { # Errors logged by Sentry's SDK itself
+ "level": "ERROR",
+ "handlers": ["console"],
+ "propagate": False,
+ },
+ {%- endif %}
+ },
+}
+
+{%- if cookiecutter.use_sentry == 'y' %}
+# Sentry
+# ------------------------------------------------------------------------------
+SENTRY_DSN = env("SENTRY_DSN")
+SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO)
+
+sentry_logging = LoggingIntegration(
+ level=SENTRY_LOG_LEVEL, # Capture info and above as breadcrumbs
+ event_level=logging.ERROR, # Send errors as events
+)
+{%- if cookiecutter.use_celery == 'y' %}
+sentry_sdk.init(
+ dsn=SENTRY_DSN,
+ integrations=[sentry_logging, DjangoIntegration(), CeleryIntegration()],
+)
+{%- else %}
+sentry_sdk.init(dsn=SENTRY_DSN, integrations=[sentry_logging, DjangoIntegration()])
+{%- endif %}
+{%- endif %}
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/test.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/test.py
index 89660a3ff..eb8d19720 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/test.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/settings/test.py
@@ -7,19 +7,15 @@ from .base import env
# GENERAL
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#debug
DEBUG = False
-# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
SECRET_KEY = env(
"DJANGO_SECRET_KEY",
default="!!!SET DJANGO_SECRET_KEY!!!",
)
-# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner
TEST_RUNNER = "django.test.runner.DiscoverRunner"
# CACHES
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
@@ -29,7 +25,6 @@ CACHES = {
# PASSWORDS
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
# TEMPLATES
@@ -46,7 +41,6 @@ TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405
# EMAIL
# ------------------------------------------------------------------------------
-# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
# Your stuff...
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/storage.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/storage.py
new file mode 100644
index 000000000..f859b9a1e
--- /dev/null
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/storage.py
@@ -0,0 +1,19 @@
+{%- if cookiecutter.cloud_provider == 'AWS' -%}
+from storages.backends.s3boto3 import S3Boto3Storage
+
+
+class StaticRootS3Boto3Storage(S3Boto3Storage):
+ location = "static"
+ default_acl = "public-read"
+
+
+class MediaRootS3Boto3Storage(S3Boto3Storage):
+ location = "media"
+ file_overwrite = False
+{%- elif cookiecutter.cloud_provider == 'Azure' %}
+from storages.backends.azure_storage import AzureStorage
+
+
+class PublicAzureStorage(AzureStorage):
+ expiration_secs = None
+{%- endif %}
diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/wsgi.py b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/wsgi.py
index 1899a30ca..3c6a9e501 100644
--- a/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/wsgi.py
+++ b/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/wsgi.py
@@ -27,8 +27,8 @@ sys.path.append(os.path.join(app_path, "{{ cookiecutter.project_slug }}"))
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
-# os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production"
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
+# os.environ["DJANGO_SETTINGS_MODULE"] = "{{ cookiecutter.project_slug }}.settings.prod"
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ cookiecutter.project_slug }}.settings.prod")
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION