Repository: imAsparky/django-cookiecutter

Issue:
[CHORE]:Review config and .env files in update to latest setup.
The Britstralian .local was copied from TB in issue-32, review if needs to be done in cookiecutter



Discussion:
It looks like prod, staging and test got a similar treatment in Britstralian issue-38

Resolving patch:
diff --git a/{{cookiecutter.git_project_name}}/config/settings/local.py b/{{cookiecutter.git_project_name}}/config/settings/local.py
--- a/{{cookiecutter.git_project_name}}/config/settings/local.py
+++ b/{{cookiecutter.git_project_name}}/config/settings/local.py
@@ -1,56 +1,101 @@
 """Django local settings for {{cookiecutter.git_project_name}} project."""
+import logging
+
+# from django.core.exceptions import ImproperlyConfigured
+from pathlib import Path
 
 import environ
 
 from .base import *  # noqa: F405 F401 F403
 
 # Read from environment variables file
-env = environ.Env(
+env = environ.FileAwareEnv(
     # set casting, default value
-    DEBUG=(bool, False)
+    DEBUG=(bool, True)
 )
-environ.Env.read_env(os.path.join(BASE_DIR, ".env/.local"))  # noqa: F405
+env.read_env(
+    Path(BASE_DIR / ".env/.local"),  # noqa: F405
+)  # noqa: F405
 
-SECRET_KEY = env("DJANGO_SECRET_KEY")
+logger = logging.getLogger(__name__)
 
-DEBUG = env("DJANGO_DEBUG", default=False)
 
-# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
+ALLOWED_HOSTS = env.list(
+    "LOCAL_ALLOWED_HOSTS",
+    default=["local.trader-biz.au"],
+)
 
-ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
+DEBUG = env("LOCAL_DJANGO_DEBUG", default=False)
 
-INTERNAL_IPS = env.list("INTERNAL_IPS")
 
-EMAIL_BACKEND = env("EMAIL_BACKEND")
+DJANGO_LOGGING_MAIL_ADMINS = env(
+    "LOCAL_DJANGO_LOGGING_MAIL_ADMINS",
+    default="ERROR",
+)
 
+DJANGO_LOGGING_LEVEL = env(
+    "LOCAL_DJANGO_LOGGING_LEVEL",
+    default="DEBUG",
+)
 
-# django-debug-toolbar
-# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
-# https://github.com/node13h/django-debug-toolbar-template-profiler
+DJANGO_LOG_FILE = env(
+    "LOCAL_DJANGO_LOG_FILE",
+    default="logging/rotating.log",
+)
 
-# Django 4.0 does not support ugettext_lazy any more.
-# Debug toolbar template profiler still requires ugettext_lazy.  Disabled until it runs on 4.0
+DJANGO_SETTINGS_MODULE = env(
+    "LOCAL_DJANGO_SETTINGS_MODULE",
+    default="config.settings.local",
+)
 
-# INSTALLED_APPS += ["template_profiler_panel"]  # noqa F405
-INSTALLED_APPS += ["debug_toolbar", "django_browser_reload"]  # noqa F405
+DJANGO_TEMPLATES_CSS = env(
+    "LOCAL_DJANGO_TEMPLATES_CSS",
+    default="/static/css/styles.css",
+)
 
-# Override the default logger level to the django environment
-# log Level settings
-LOGGING["loggers"][""]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
-)  # noqa: F405
-LOGGING["handlers"]["stdout"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
-)  # noqa: F405
-LOGGING["handlers"]["rotated_logs"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
+
+DATABASES = {
+    "default": {
+        "ENGINE": "django.db.backends.sqlite3",
+        "NAME": BASE_DIR / "db.sqlite3",  # noqa: F405
+    }
+}
+
+EMAIL_BACKEND = env(
+    "LOCAL_EMAIL_BACKEND",
+    default="django.core.mail.backends.console.EmailBackend",
 )
 
-MIDDLEWARE += [
-    "debug_toolbar.middleware.DebugToolbarMiddleware",
-    "django_browser_reload.middleware.BrowserReloadMiddleware",
-]  # noqa F405
 
+# Default empty string required for Docker build to succeed.
+EMAIL_HOST = env(
+    "LOCAL_EMAIL_HOST",
+    default="",
+)
+
+
+EMAIL_HOST_PASSWORD = env(
+    "LOCAL_EMAIL_HOST_PASSWORD",
+    default="",
+)
+
+EMAIL_HOST_USER = env(
+    "LOCAL_EMAIL_HOST_USER",
+    default="",
+)
+EMAIL_PORT = env(
+    "LOCAL_EMAIL_PORT",
+    default="",
+)
+EMAIL_USE_TLS = env(
+    "LOCAL_EMAIL_USE_TLS",
+    default="",
+)
+
+INSTALLED_APPS += [  # noqa: F405
+    "django_browser_reload",
+    "debug_toolbar",
+]
 DEBUG_TOOLBAR_PANELS = [
     "debug_toolbar.panels.history.HistoryPanel",
     "debug_toolbar.panels.versions.VersionsPanel",
@@ -73,9 +118,114 @@
     "DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"],
 }
 
-DATABASES = {
-    "default": {
-        "ENGINE": "django.db.backends.sqlite3",
-        "NAME": BASE_DIR / "db.sqlite3",  # noqa: F405
+
+INTERNAL_IPS = env.list(
+    "LOCAL_DJANGO_INTERNAL_IPS",
+    default=[],
+)
+
+# Override the default logger level to the django environment
+# log Level settings
+LOGGING["loggers"][""]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["stdout"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"][
+    "level"
+] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["filename"] = DJANGO_LOG_FILE  # noqa: F405
+
+MIDDLEWARE += [  # noqa: F405
+    "django_browser_reload.middleware.BrowserReloadMiddleware",
+    "debug_toolbar.middleware.DebugToolbarMiddleware",
+]
+
+SECRET_KEY = env(
+    "LOCAL_DJANGO_SECRET_KEY",
+    default="!!!INSECURE_LOCAL_SECRET!!!",
+)
+
+
+# Check it is safe to run in local.
+assert (  # nosec
+    env("LOCAL_DJANGO_SECRET_KEY") != "!!!INSECURE_LOCAL_SECRET!!!"
+), "The DJANGO_SECRET_KEY must be set for local."
+
+TAILWIND_CSS_DEV = env(
+    "LOCAL_TAILWIND_CSS_DEV",
+    default=True,
+)
+
+
+# `USE_STATIC` options are `Local` or `S3`
+USE_STATIC = env("LOCAL_USE_STATIC", default="Local")
+
+# Digital Ocean S3 Storage Configuration
+if USE_STATIC == "S3":
+    AWS_ACCESS_KEY_ID = env(
+        "LOCAL_AWS_ACCESS_KEY_ID",
+        default="LOCAL_AWS_KEY_NOT_SET",
+    )
+    AWS_SECRET_ACCESS_KEY = env(
+        "LOCAL_AWS_SECRET_ACCESS_KEY",
+        default="LOCAL_AWS_SECRET_NOT_SET",
+    )
+
+    AWS_S3_REGION_NAME = env(
+        "LOCAL_AWS_S3_REGION_NAME",
+        default="syd1",
+    )
+    AWS_S3_ENDPOINT_URL = env(
+        "LOCAL_AWS_S3_ENDPOINT_URL",
+        default=f"https://{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+    )
+    AWS_STORAGE_BUCKET_NAME = env(
+        "LOCAL_AWS_STORAGE_BUCKET_NAME",
+        default="tb-s3",
+    )
+    AWS_LOCATION = env(
+        "LOCAL_AWS_LOCATION",
+        default=f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+    )
+    # This isnt working when using an ENV VAR
+    # AWS_S3_OBJECT_PARAMETERS = env(
+    #     "LOCAL_AWS_S3_OBJECT_PARAMETERS",
+    #     {"CacheControl": "max-age=1"},
+    # )
+    AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=1"}
+
+    STORAGES = {
+        "default": {
+            "BACKEND": env(
+                "DEFAULT_FILE_STORAGE",
+                default="core.storage.backends.MediaRootS3Boto3Storage",
+            )
+        },
+        "staticfiles": {
+            "BACKEND": env(
+                "STATICFILES_STORAGE",
+                default="core.storage.backends.StaticRootS3Boto3Storage",
+            )
+        },
     }
-}
+
+    # Static url must end with default STATIC_URL from env var or base.py added to
+    # S3 storage location.
+    STATIC_URL = env("LOCAL_STATIC_URL", default="static-lo/")
+
+    # Media url must end with default MEDIA_URL from env var or base.py added to
+    # S3 storage location.
+    MEDIA_URL = env("LOCAL_MEDIA_URL", default="media-lo/")
+
+    # Set the url for the css file
+    LOCAL_DJANGO_TEMPLATES_CSS = f"{STATIC_URL}css/styles.css"
+
+#     elif USE_STATIC != "S3":
+#         raise ImproperlyConfigured(
+#             "Staging environment USE_STATIC must be S3, it is configured to %s."
+#             % (USE_STATIC),
+#         )
+
+# except ImproperlyConfigured:
+#     logger.critical(
+#         "Staging environment USE_STATIC must be S3, it is configured to %s."
+#         % (USE_STATIC),
+#     )
diff --git a/{{cookiecutter.git_project_name}}/config/settings/production.py b/{{cookiecutter.git_project_name}}/config/settings/production.py
--- a/{{cookiecutter.git_project_name}}/config/settings/production.py
+++ b/{{cookiecutter.git_project_name}}/config/settings/production.py
@@ -2,6 +2,7 @@
 
 import environ
 from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
 
 from .base import *  # noqa: F405 F401 F403
 {% if cookiecutter.deploy_with_docker == "swarm" %}
@@ -13,39 +14,177 @@
 )
 environ.Env.read_env(os.path.join(BASE_DIR, ".env/.production"))  # noqa: F405
 {% endif %}
-# fmt: off
-SECRET_KEY = env('DJANGO_SECRET_KEY', default="!!!INSECURE_PRODUCTION_SECRET!!!")
 
-DEBUG = env('DJANGO_DEBUG', default=False)
-# fmt: on
+logger = logging.getLogger(__name__)
 
-ALLOWED_HOSTS = ["{{cookiecutter.ALLOWED_HOSTS}}"]
 
-INTERNAL_IPS = ["{{cookiecutter.INTERNAL_IPS}}"]
+ALLOWED_HOSTS = env.list(
+    "PROD_ALLOWED_HOSTS",
+    default=["{{cookiecutter.ALLOWED_HOSTS}}"],
+)
+
+DEBUG = env("PROD_DJANGO_DEBUG", default=False)
+
+
+DJANGO_LOGGING_MAIL_ADMINS = env(
+    "PROD_DJANGO_LOGGING_MAIL_ADMINS",
+    default="ERROR",
+)
+
+DJANGO_LOGGING_LEVEL = env(
+    "PROD_DJANGO_LOGGING_LEVEL",
+    default="INFO",
+)
+
+DJANGO_LOG_FILE = env(
+    "PROD_DJANGO_LOG_FILE",
+    default="logging/rotating.log",
+)
+
+DJANGO_SETTINGS_MODULE = env(
+    "PROD_DJANGO_SETTINGS_MODULE",
+    default="config.settings.production",
+)
+
+
+DATABASES = {
+    "default": env.db(
+        "PROD_DATABASE_URL",
+    ),
+}
 
 EMAIL_BACKEND = env(
-    "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
+    "PROD_EMAIL_BACKEND",
+    default="django.core.mail.backends.console.EmailBackend",
+)
+
+
+# Default empty string required for Docker build to succeed.
+EMAIL_HOST = env(
+    "PROD_EMAIL_HOST",
+    default="",
 )
 
-DATABASES = {"default": env.db()}
+
+EMAIL_HOST_PASSWORD = env(
+    "PROD_EMAIL_HOST_PASSWORD",
+    default="",
+)
+
+EMAIL_HOST_USER = env(
+    "PROD_EMAIL_HOST_USER",
+    default="",
+)
+EMAIL_PORT = env(
+    "PROD_EMAIL_PORT",
+    default="",
+)
+EMAIL_USE_TLS = env(
+    "PROD_EMAIL_USE_TLS",
+    default="",
+)
+
+INTERNAL_IPS = env.list(
+    "PROD_INTERNAL_IPS",
+    default=["{{cookiecutter.INTERNAL_IPS}}"],
+)
 
 # Override the default logger level to the django environment
 # log Level settings
-LOGGING["loggers"][""]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_PRODUCTION", default="WARNING"
-)  # noqa: F405
-LOGGING["handlers"]["stdout"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_PRODUCTION", default="WARNING"
-)  # noqa: F405
-LOGGING["handlers"]["rotated_logs"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_PRODUCTION", default="WARNING"
+LOGGING["loggers"][""]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["stdout"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["filename"] = DJANGO_LOG_FILE  # noqa: F405
+
+
+SECRET_KEY = env(
+    "PROD_DJANGO_SECRET_KEY",
+    default="!!!INSECURE_PRODUCTION_SECRET!!!",
 )
 
 
 # Check it is safe to run in production.
 assert (  # nosec
-    not settings.DEBUG
-), "DEBUG mode should be False for the production environment."
-assert (  # nosec
-    env("DJANGO_SECRET_KEY") != "!!!INSECURE_PRODUCTION_SECRET!!!"
+    env("PROD_DJANGO_SECRET_KEY") != "!!!INSECURE_PRODUCTION_SECRET!!!"
 ), "The DJANGO_SECRET_KEY must be set for production."
+
+assert (  # nosec
+    DEBUG is False
+), "Production can't be run in DEBUG mode for security reasons."
+
+# `USE_STATIC` options are `local` or `S3`
+USE_STATIC = env("PROD_USE_STATIC", default="S3")
+
+
+try:
+    # Digital Ocean S3 Storage Configuration
+    if USE_STATIC == "S3":
+        AWS_ACCESS_KEY_ID = env(
+            "PROD_AWS_ACCESS_KEY_ID",
+            default="PRODUCTION_AWS_KEY_NOT_SET",
+        )
+        AWS_SECRET_ACCESS_KEY = env(
+            "PROD_AWS_SECRET_ACCESS_KEY",
+            default="PROD_AWS_SECRET_NOT_SET",
+        )
+
+        AWS_S3_REGION_NAME = env(
+            "PROD_AWS_S3_REGION_NAME",
+            default="syd1",
+        )
+        AWS_S3_ENDPOINT_URL = env(
+            "PROD_AWS_S3_ENDPOINT_URL",
+            default=f"https://{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        AWS_STORAGE_BUCKET_NAME = env(
+            "PROD_AWS_STORAGE_BUCKET_NAME",
+            default="tb-s3",
+        )
+        AWS_LOCATION = env(
+            "PROD_AWS_LOCATION",
+            default=f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        # This isnt working when using an ENV VAR
+        # AWS_S3_OBJECT_PARAMETERS = env(
+        #     "PROD_AWS_S3_OBJECT_PARAMETERS",
+        #     {"CacheControl": "max-age=1"},
+        # )
+        AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=1"}
+
+        STORAGES = {
+            "default": {
+                "BACKEND": env(
+                    "DEFAULT_FILE_STORAGE",
+                    default="core.storage.backends.MediaRootS3Boto3Storage",
+                )
+            },
+            "staticfiles": {
+                "BACKEND": env(
+                    "STATICFILES_STORAGE",
+                    default="core.storage.backends.StaticRootS3Boto3Storage",
+                )
+            },
+        }
+
+        # Static url must end with default STATIC_URL from env var or base.py added to
+        # S3 storage location.
+        STATIC_URL = env("PROD_STATIC_URL", default="static/")
+
+        # Media url must end with default MEDIA_URL from env var or base.py added to
+        # S3 storage location.
+        MEDIA_URL = env("PROD_MEDIA_URL", default="media/")
+
+        # Set the url for the css file
+        PROD_DJANGO_TEMPLATES_CSS = f"{STATIC_URL}css/styles.css"
+
+    elif USE_STATIC != "S3":
+        raise ImproperlyConfigured(
+            "Production environment USE_STATIC must be S3, it is configured to %s."
+            % (USE_STATIC),
+        )
+
+except ImproperlyConfigured:
+    logger.critical(
+        "Production environment USE_STATIC must be S3, it is configured to %s."
+        % (USE_STATIC),
+    )
diff --git a/{{cookiecutter.git_project_name}}/config/settings/staging.py b/{{cookiecutter.git_project_name}}/config/settings/staging.py
--- a/{{cookiecutter.git_project_name}}/config/settings/staging.py
+++ b/{{cookiecutter.git_project_name}}/config/settings/staging.py
@@ -2,6 +2,7 @@
 
 import environ
 from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
 
 from .base import *  # noqa: F405 F401 F403
 {% if cookiecutter.deploy_with_docker == "swarm" %}
@@ -13,39 +14,174 @@
 )
 environ.Env.read_env(os.path.join(BASE_DIR, ".env/.staging"))  # noqa: F405
 {% endif %}
-# fmt: off
-SECRET_KEY = env('DJANGO_SECRET_KEY', default="!!!INSECURE_PRODUCTION_SECRET!!!")
+logger = logging.getLogger(__name__)
 
-DEBUG = env('DJANGO_DEBUG', default=False)
-# fmt: on
 
-ALLOWED_HOSTS = ["{{cookiecutter.ALLOWED_HOSTS}}"]
+ALLOWED_HOSTS = env.list(
+    "STAGING_ALLOWED_HOSTS",
+    default=["{{cookiecutter.ALLOWED_HOSTS}}"],
+)
+
+DEBUG = env("STAGING_DJANGO_DEBUG", default=False)
+
+
+DJANGO_LOGGING_MAIL_ADMINS = env(
+    "STAGING_DJANGO_LOGGING_MAIL_ADMINS",
+    default="ERROR",
+)
 
-INTERNAL_IPS = ["{{cookiecutter.INTERNAL_IPS}}"]
+DJANGO_LOGGING_LEVEL = env(
+    "STAGING_DJANGO_LOGGING_LEVEL",
+    default="INFO",
+)
+
+DJANGO_LOG_FILE = env(
+    "STAGING_DJANGO_LOG_FILE",
+    default="logging/rotating.log",
+)
+
+DJANGO_SETTINGS_MODULE = env(
+    "STAGING_DJANGO_SETTINGS_MODULE",
+    default="config.settings.staging",
+)
+
+
+DATABASES = {
+    "default": env.db(
+        "STAGING_DATABASE_URL",
+    ),
+}
 
 EMAIL_BACKEND = env(
-    "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
+    "STAGING_EMAIL_BACKEND",
+    default="django.core.mail.backends.console.EmailBackend",
+)
+
+
+# Default empty string required for Docker build to succeed.
+EMAIL_HOST = env(
+    "STAGING_EMAIL_HOST",
+    default="",
+)
+
+
+EMAIL_HOST_PASSWORD = env(
+    "STAGING_EMAIL_HOST_PASSWORD",
+    default="",
+)
+
+EMAIL_HOST_USER = env(
+    "STAGING_EMAIL_HOST_USER",
+    default="",
+)
+EMAIL_PORT = env(
+    "STAGING_EMAIL_PORT",
+    default="",
+)
+EMAIL_USE_TLS = env(
+    "STAGING_EMAIL_USE_TLS",
+    default="",
 )
 
-DATABASES = {"default": env.db()}
+INTERNAL_IPS = env.list(
+    "STAGING_INTERNAL_IPS",
+    default=["{{cookiecutter.INTERNAL_IPS}}"],
+)
 
 # Override the default logger level to the django environment
 # log Level settings
-LOGGING["loggers"][""]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_STAGING", default="WARNING"
-)  # noqa: F405
-LOGGING["handlers"]["stdout"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_STAGING", default="WARNING"
-)  # noqa: F405
-LOGGING["handlers"]["rotated_logs"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL_STAGING", default="WARNING"
+LOGGING["loggers"][""]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["stdout"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["filename"] = DJANGO_LOG_FILE  # noqa: F405
+
+
+SECRET_KEY = env(
+    "STAGING_DJANGO_SECRET_KEY",
+    default="!!!INSECURE_STAGING_SECRET!!!",
 )
 
 
-# Check it is safe to run in production.
-assert (  # nosec
-    not settings.DEBUG
-), "DEBUG mode should be False for the production environment."
+# Check it is safe to run in staging.
 assert (  # nosec
-    env("DJANGO_SECRET_KEY") != "!!!INSECURE_PRODUCTION_SECRET!!!"
-), "The DJANGO_SECRET_KEY must be set for production."
+    env("STAGING_DJANGO_SECRET_KEY") != "!!!INSECURE_STAGING_SECRET!!!"
+), "The DJANGO_SECRET_KEY must be set for staging."
+
+
+# `USE_STATIC` options are `local` or `S3`
+USE_STATIC = env("STAGING_USE_STATIC", default="Local")
+
+
+try:
+    # Digital Ocean S3 Storage Configuration
+    if USE_STATIC == "S3":
+        AWS_ACCESS_KEY_ID = env(
+            "STAGING_AWS_ACCESS_KEY_ID",
+            default="STAGING_AWS_KEY_NOT_SET",
+        )
+        AWS_SECRET_ACCESS_KEY = env(
+            "STAGING_AWS_SECRET_ACCESS_KEY",
+            default="STAGING_AWS_SECRET_NOT_SET",
+        )
+
+        AWS_S3_REGION_NAME = env(
+            "STAGING_AWS_S3_REGION_NAME",
+            default="syd1",
+        )
+        AWS_S3_ENDPOINT_URL = env(
+            "STAGING_AWS_S3_ENDPOINT_URL",
+            default=f"https://{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        AWS_STORAGE_BUCKET_NAME = env(
+            "STAGING_AWS_STORAGE_BUCKET_NAME",
+            default="tb-s3",
+        )
+        AWS_LOCATION = env(
+            "STAGING_AWS_LOCATION",
+            default=f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        # This isnt working when using an ENV VAR
+        # AWS_S3_OBJECT_PARAMETERS = env(
+        #     "STAGING_AWS_S3_OBJECT_PARAMETERS",
+        #     {"CacheControl": "max-age=1"},
+        # )
+        AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=1"}
+
+        STORAGES = {
+            "default": {
+                "BACKEND": env(
+                    "DEFAULT_FILE_STORAGE",
+                    default="core.storage.backends.MediaRootS3Boto3Storage",
+                )
+            },
+            "staticfiles": {
+                "BACKEND": env(
+                    "STATICFILES_STORAGE",
+                    default="core.storage.backends.StaticRootS3Boto3Storage",
+                )
+            },
+        }
+
+        # Static url must end with default STATIC_URL from env var or base.py added to
+        # S3 storage location.
+
+        STATIC_URL = env("STAGING_STATIC_URL", default="static-st/")
+
+        # Media url must end with default MEDIA_URL from env var or base.py added to
+        # S3 storage location.
+        MEDIA_URL = env("STAGING_MEDIA_URL", default="media-st/")
+
+        # Set the url for the css file
+        STAGING_DJANGO_TEMPLATES_CSS = f"{STATIC_URL}css/styles.css"
+
+    elif USE_STATIC != "S3":
+        raise ImproperlyConfigured(
+            "Staging environment USE_STATIC must be S3, it is configured to %s."
+            % (USE_STATIC),
+        )
+
+except ImproperlyConfigured:
+    logger.critical(
+        "Staging environment USE_STATIC must be S3, it is configured to %s."
+        % (USE_STATIC),
+    )


Test patch:
diff --git a/tests/test_bake_django.py b/tests/test_bake_django.py
--- a/tests/test_bake_django.py
+++ b/tests/test_bake_django.py
@@ -947,13 +947,19 @@ def test_baked_django_settings_production_file_ok(cookies):
     default_django = cookies.bake()
 
     settings_path = default_django.project_path / "config/settings/production.py"
+
     settings_file = settings_path.read_text().splitlines()
 
+
     assert (
         '"""Django production settings for django-boilerplate project."""'
         in settings_file
     )
-    assert 'ALLOWED_HOSTS = [""]' in settings_file
+    '    default=["trader-biz.au"],'
+    assert '    "PROD_ALLOWED_HOSTS",' in settings_file
+    assert '    default=[""],' in settings_file
+    assert ')' in settings_file
+
 
 
 def test_baked_django_settings_staging_file_ok(cookies):
@@ -966,7 +972,9 @@ def test_baked_django_settings_staging_file_ok(cookies):
     assert (
         '"""Django staging settings for django-boilerplate project."""' in settings_file
     )
-    assert 'ALLOWED_HOSTS = [""]' in settings_file
+    assert '    "STAGING_ALLOWED_HOSTS",' in settings_file
+    assert '    default=[""],' in settings_file
+    assert ')' in settings_file
 
 
 def test_baked_django_settings_test_file_ok(cookies):
@@ -977,7 +985,9 @@ def test_baked_django_settings_test_file_ok(cookies):
     settings_file = settings_path.read_text().splitlines()
 
     assert '"""Django test settings for django-boilerplate project."""' in settings_file
-    assert 'ALLOWED_HOSTS = [""]' in settings_file
+    assert '    "TESTING_ALLOWED_HOSTS",' in settings_file
+    assert '    default=[""],' in settings_file
+    assert ')' in settings_file
 
 
 def test_baked_django_tox_file_ok(cookies):
diff --git a/{{cookiecutter.git_project_name}}/.env/.testing b/{{cookiecutter.git_project_name}}/.env/.testing
--- a/{{cookiecutter.git_project_name}}/.env/.testing
+++ b/{{cookiecutter.git_project_name}}/.env/.testing
@@ -1,20 +1,55 @@
-DJANGO_DEBUG=FALSE
-DJANGO_LOG_FILE="logging/rotating_testing.log"
-DJANGO_LOGGING_LEVEL="DEBUG"
-DJANGO_LOGGING_MAIL_ADMINS="ERROR"
+# Testing Environment ENV Keys
+
+# Testing Environment Django
+TESTING_ALLOWED_HOSTS=*
+# Testing database options: sqlite3, other.
+# If using other, a TESTING_DATABASE_URL connection string must be supplied.
+TESTING_DJANGO_DATABASE=sqlite3
+TESTING_DATABASE_URL=""
+TESTING_DJANGO_DEBUG=False
+TESTING_DJANGO_INTERNAL_IPS=[]
+TESTING_DJANGO_LOG_FILE='logging/rotating_testing.log'
+TESTING_DJANGO_LOGGING_LEVEL='DEBUG'
+TESTING_DJANGO_LOGGING_MAIL_ADMINS='ERROR'
+# For the docker image
+TESTING_DJANGO_MANAGEPY_MIGRATE=on
+TESTING_DJANGO_SECRET_KEY='!!!DODGY_TESTING_SECRET!!!'
+TESTING_DJANGO_SETTINGS_MODULE='config.settings.testing'
+TESTING_DJANGO_TEMPLATES_CSS="/static/css/styles.css"
+TESTING_MEDIA_URL='media-lo/'
+TESTING_STATIC_URL='static-lo/'
+# static options are Local or S3
+TESTING_USE_STATIC='S3'
+TESTING_TAILWIND_CSS_DEV=False
+
+# Testing Environment S3 access
+TESTING_AWS_ACCESS_KEY_ID=""
+# If using the default leave ENDPOINT_URL unset
+TESTING_AWS_S3_ENDPOINT_URL=""
+# If using the default leave AWS_LOCATION unset
+TESTING_AWS_LOCATION=""
+TESTING_AWS_SECRET_ACCESS_KEY=""
+TESTING_AWS_S3_REGION_NAME=""
+TESTING_AWS_S3_OBJECT_PARAMETERS=""
+TESTING_AWS_STORAGE_BUCKET_NAME=""
+
+# Testing Environment Email Settings
+TESTING_EMAIL_BACKEND=""
+TESTING_EMAIL_HOST=""
+TESTING_EMAIL_PORT=""
+TESTING_EMAIL_HOST_USER=""
+TESTING_EMAIL_HOST_PASSWORD=""
+TESTING_EMAIL_USE_TLS=""
+
+
 DJANGO_MANAGEPY_MIGRATE=on
-DJANGO_SECRET_KEY="!!!INSECURE_TESTING_SECRET!!!"
-DJANGO_SETTINGS_MODULE=config.settings.test
+
+# Testing Environment Database if not using URL
 DB_ENGINE=""
 DB_NAME=""
 DB_USER=""
 DB_PASSWORD=""
 DB_HOST=""
 DB_PORT=""
-ALLOWED_HOSTS=""
-INTERNAL_IPS=""
 
-# Testing database options: sqlite3, other.
-# If using other, a DB_URL connection string must be supplied.
-TESTING_DATABASE=sqlite3
-DB_URL=""
+
diff --git a/{{cookiecutter.git_project_name}}/config/settings/test.py b/{{cookiecutter.git_project_name}}/config/settings/test.py
--- a/{{cookiecutter.git_project_name}}/config/settings/test.py
+++ b/{{cookiecutter.git_project_name}}/config/settings/test.py
@@ -2,53 +2,140 @@
 
 import environ
 from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
 
 from .base import *  # noqa: F405 F401 F403
 
 # Read from environment variables file
-env = environ.Env(
+env = environ.FileAwareEnv(
     # set casting, default value
-    DEBUG=(bool, False)
+    DEBUG=(bool, True)
 )
-environ.Env.read_env(os.path.join(BASE_DIR, ".env/.testing"))  # noqa: F405
+env.read_env(
+    Path(BASE_DIR / ".env/.testing"),  # noqa: F405
+)
+logger = logging.getLogger(__name__)
 
-SETTINGS_MODULE = env("DJANGO_SETTINGS_MODULE", default="config.settings.test")
 
-SECRET_KEY = env("DJANGO_SECRET_KEY")
+ALLOWED_HOSTS = env.list(
+    "TESTING_ALLOWED_HOSTS",
+    default=["{{cookiecutter.ALLOWED_HOSTS}}"],
+)
 
-DEBUG = env("DJANGO_DEBUG", default=False)
+DEBUG = env("TESTING_DJANGO_DEBUG", default=False)
 
-ALLOWED_HOSTS = [""]
 
-INTERNAL_IPS = [""]
+DJANGO_LOGGING_MAIL_ADMINS = env(
+    "TESTING_DJANGO_LOGGING_MAIL_ADMINS",
+    default="ERROR",
+)
 
-# Override the default logger level to the django environment
-# log Level settings
-LOGGING["loggers"][""]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
-)  # noqa: F405
-LOGGING["handlers"]["stdout"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
-)  # noqa: F405
-LOGGING["handlers"]["rotated_logs"]["level"] = env(  # noqa F405
-    "DJANGO_LOGGING_LEVEL", default="DEBUG"
+DJANGO_LOGGING_LEVEL = env(
+    "TESTING_DJANGO_LOGGING_LEVEL",
+    default="DEBUG",
+)
+
+DJANGO_LOG_FILE = env(
+    "TESTING_DJANGO_LOG_FILE",
+    default="logging/rotating_testing.log",
+)
+
+DJANGO_SETTINGS_MODULE = env(
+    "TESTING_DJANGO_SETTINGS_MODULE",
+    default="config.settings.test",
 )
 
+DJANGO_TEMPLATES_CSS = env(
+    "TESTING_DJANGO_TEMPLATES_CSS",
+    default="/static/css/styles.css",
+)
+
+
 # Selects which database to use for testing, default=sqlite3 .
-TESTING_DATABASE = env("TESTING_DATABASE")
+TESTING_DATABASE = env(
+    "TESTING_DJANGO_DATABASE",
+    default="sqlite3",
+)
 
-if TESTING_DATABASE == "sqlite3":
 
+if TESTING_DATABASE == "sqlite3":
     DATABASES = {
         "default": {
             "ENGINE": "django.db.backends.sqlite3",
-            # "NAME": BASE_DIR / "db.sqlite3",
+            "NAME": BASE_DIR / "db.sqlite3",  # noqa: F405
         }
     }
 
 else:
+    DATABASES = {
+        "default": env.db(
+            "TESTING_DATABASE_URL",
+        ),
+    }
+
+
+EMAIL_BACKEND = env(
+    "TESTING_EMAIL_BACKEND",
+    default="django.core.mail.backends.console.EmailBackend",
+)
+
+
+# Default empty string required for Docker build to succeed.
+EMAIL_HOST = env(
+    "TESTING_EMAIL_HOST",
+    default="",
+)
+
 
-    DATABASES = {"default": env.db()}
+EMAIL_HOST_PASSWORD = env(
+    "TESTING_EMAIL_HOST_PASSWORD",
+    default="",
+)
+
+EMAIL_HOST_USER = env(
+    "TESTING_EMAIL_HOST_USER",
+    default="",
+)
+EMAIL_PORT = env(
+    "TESTING_EMAIL_PORT",
+    default="",
+)
+EMAIL_USE_TLS = env(
+    "TESTING_EMAIL_USE_TLS",
+    default="",
+)
+
+INTERNAL_IPS = env.list(
+    "TESTING_INTERNAL_IPS",
+    default=["{{cookiecutter.INTERNAL_IPS}}"],
+)
+
+# Override the default logger level to the django environment
+# log Level settings
+LOGGING["loggers"][""]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["stdout"]["level"] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"][  # noqa: F405
+    "level"
+] = DJANGO_LOGGING_LEVEL  # noqa: F405
+LOGGING["handlers"]["rotated_logs"]["filename"] = DJANGO_LOG_FILE  # noqa: F405
+
+
+SECRET_KEY = env(
+    "TESTING_DJANGO_SECRET_KEY",
+    default="!!!INSECURE_TESTING_SECRET!!!",
+)
+
+# Check it is safe to run in testing.
+assert (  # nosec
+    SECRET_KEY != "!!!INSECURE_TESTING_SECRET!!!"
+), "The DJANGO_SECRET_KEY must be set for testing."
+
+TAILWIND_CSS_DEV = env(
+    "TESTING_TAILWIND_CSS_DEV",
+    default=False,
+)
+# `USE_STATIC` options are `local` or `S3`
+USE_STATIC = env("TESTING_USE_STATIC", default="Local")
 
 # Test with DEBUG off can speed up tests.
 assert not settings.DEBUG, "DEBUG mode should be off for testing."  # nosec
@@ -60,3 +147,77 @@
 PASSWORD_HASHERS = [
     "django.contrib.auth.hashers.MD5PasswordHasher",
 ]
+
+
+try:
+    # Digital Ocean S3 Storage Configuration
+    if USE_STATIC == "S3":
+        AWS_ACCESS_KEY_ID = env(
+            "TESTING_AWS_ACCESS_KEY_ID",
+            default="TESTING_AWS_KEY_NOT_SET",
+        )
+        AWS_SECRET_ACCESS_KEY = env(
+            "TESTING_AWS_SECRET_ACCESS_KEY",
+            default="TESTING_AWS_SECRET_NOT_SET",
+        )
+
+        AWS_S3_REGION_NAME = env(
+            "TESTING_AWS_S3_REGION_NAME",
+            default="syd1",
+        )
+        AWS_S3_ENDPOINT_URL = env(
+            "TESTING_AWS_S3_ENDPOINT_URL",
+            default=f"https://{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        AWS_STORAGE_BUCKET_NAME = env(
+            "TESTING_AWS_STORAGE_BUCKET_NAME",
+            default="tb-s3",
+        )
+        AWS_LOCATION = env(
+            "TESTING_AWS_LOCATION",
+            default=f"https://{AWS_STORAGE_BUCKET_NAME}.{AWS_S3_REGION_NAME}.digitaloceanspaces.com",
+        )
+        # This isnt working when using an ENV VAR
+        # AWS_S3_OBJECT_PARAMETERS = env(
+        #     "TESTING_AWS_S3_OBJECT_PARAMETERS",
+        #     {"CacheControl": "max-age=1"},
+        # )
+        AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=1"}
+
+        STORAGES = {
+            "default": {
+                "BACKEND": env(
+                    "DEFAULT_FILE_STORAGE",
+                    default="core.storage.backends.MediaRootS3Boto3Storage",
+                )
+            },
+            "staticfiles": {
+                "BACKEND": env(
+                    "STATICFILES_STORAGE",
+                    default="core.storage.backends.StaticRootS3Boto3Storage",
+                )
+            },
+        }
+
+        # Static url must end with default STATIC_URL from env var or base.py added to
+        # S3 storage location.
+        STATIC_URL = env("TESTING_STATIC_URL", default="static-lo/")
+
+        # Media url must end with default MEDIA_URL from env var or base.py added to
+        # S3 storage location.
+        MEDIA_URL = env("TESTING_MEDIA_URL", default="media-lo/")
+
+        # Set the url for the css file
+        TESTING_DJANGO_TEMPLATES_CSS = f"{STATIC_URL}css/styles.css"
+
+    elif USE_STATIC != "S3":
+        raise ImproperlyConfigured(
+            "Testing environment USE_STATIC must be S3, it is configured to %s."
+            % (USE_STATIC),
+        )
+
+except ImproperlyConfigured:
+    logger.critical(
+        "Testing environment USE_STATIC must be S3, it is configured to %s."
+        % (USE_STATIC),
+    )