1

I'm building a Django app but I've got an issue. I've imported the environ package in order to store all my variables in a .env file, but actually it seems my app doesn't read it and I can't get why.

Here is my .env file:

DEBUG=True
SECRET_KEY='<SECRET>'
DB_NAME=<SECRET>
DB_USER=<SECRET>
DB_PASSWORD=<SECRET>
DB_HOST=localhost
DB_PORT=
EMAIL_HOST=
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
EMAIL_PORT=
DEFAULT_FROM_EMAIL=<SECRET>

Here is my settings.py file:

from pathlib import Path
import environ

env = environ.Env(
    DEBUG=(bool, False)
)

READ_DOT_ENV_FILE = env.bool('READ_DOT_ENV_FILE', default=False)
if READ_DOT_ENV_FILE:
    environ.Env.read_env()

DEBUG = env('DEBUG')
SECRET_KEY = env('SECRET_KEY')

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'whitenoise.runserver_nostatic',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third party apps
    'crispy_forms',
    'crispy_tailwind',
    'tailwind',
    'theme',

    # Local apps
    'leads',
    'agents',
]

MIDDLEWARE = [
    'whitenoise.middleware.WhiteNoiseMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'djcrm.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [ BASE_DIR / "templates" ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'djcrm.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': env("DB_NAME"),
        'USER': env("DB_USER"),
        'PASSWORD': env("DB_PASSWORD"),
        'HOST': env("DB_HOST"),
        'PORT': env("DB_PORT"),
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static"
]
MEDIA_URL = '/media/'
MEDIA_ROOT = "media_root"
STATIC_ROOT = "static_root"
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

AUTH_USER_MODEL = 'leads.User'
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
LOGIN_REDIRECT_URL = "/leads"
LOGIN_URL = "/login"
LOGOUT_REDIRECT_URL = "/"

CRISPY_ALLOWED_TEMPLATE_PACKS = "tailwind"
CRISPY_TEMPLATE_PACK = 'tailwind'

if not DEBUG:
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
    SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    SECURE_HSTS_SECONDS = 31536000  # 1 year
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_HSTS_PRELOAD = True
    X_FRAME_OPTIONS = "DENY"

    ALLOWED_HOSTS = ["*"]

    EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
    EMAIL_HOST = env("EMAIL_HOST")
    EMAIL_HOST_USER = env("EMAIL_HOST_USER")
    EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")
    EMAIL_USE_TLS = True
    EMAIL_PORT = env("EMAIL_PORT")
    DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL")


LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'WARNING',
    },
}

TAILWIND_APP_NAME = 'theme'

When I run the python manage.py runserver command (or any other command btw), I get this error:

Traceback (most recent call last):
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/environ/environ.py", line 273, in get_value
    value = self.ENVIRON[var]
  File "/opt/homebrew/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'SECRET_KEY'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/davidemancuso/Dev/django-crm/manage.py", line 22, in <module>
    main()
  File "/Users/davidemancuso/Dev/django-crm/manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/base.py", line 330, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 61, in execute
    super().execute(*args, **options)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/base.py", line 371, in execute
    output = self.handle(*args, **options)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 68, in handle
    if not settings.DEBUG and not settings.ALLOWED_HOSTS:
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/conf/__init__.py", line 83, in __getattr__
    self._setup(name)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/conf/__init__.py", line 70, in _setup
    self._wrapped = Settings(settings_module)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/django/conf/__init__.py", line 177, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/opt/homebrew/Cellar/python@3.9/3.9.2_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 790, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/Users/davidemancuso/Dev/django-crm/djcrm/settings.py", line 13, in <module>
    SECRET_KEY = env('SECRET_KEY')
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/environ/environ.py", line 123, in __call__
    return self.get_value(var, cast=cast, default=default, parse_default=parse_default)
  File "/Users/davidemancuso/Dev/django-crm/env/lib/python3.9/site-packages/environ/environ.py", line 277, in get_value
    raise ImproperlyConfigured(error_msg)
django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable

Now, if I put the secret key hard-coded into the settings.py, the error changes in:

django.core.exceptions.ImproperlyConfigured: Set the DB_NAME environment variable

As far as I can see, it seems the settings.py is not reading at all the variables in the .env, but I don't know how to solve this. What can I do?

EDIT: PROBLEM SOLVED It was easier than I thought. I forgot to type in the terminal export READ_DOT_ENV_FILE=True in order to enable the file reading in development.

Thanks to all who try to help me!

Have a nice day

Davide
  • 435
  • 1
  • 5
  • 12
  • Python doesn't know it has to read your .env file to load environment variables. You can 1) export your env variables when activating your python virtual environment but that will required you having to customize your activate script, or 2) use [python-dotenv](https://pypi.org/project/python-dotenv/) to load your .env file. – Scratch'N'Purr May 16 '21 at 07:21
  • You read that file only if `READ_DOT_ENV_FILE` is present (according to this condition `if READ_DOT_ENV_FILE:`), which I see is not there in your env file. – Abdul Aziz Barkat May 16 '21 at 07:21
  • @AbdulAzizBarkat well it wouldn't make sense to have that in the file, because the file is only read if it's set. But OP that seems like an obvious thing to check: the file is loaded conditionally, is that condition true? – jonrsharpe May 16 '21 at 07:22
  • @Scratch'N'Purr the OP appears to be using https://pypi.org/project/python-environ/, which does load .env files. – jonrsharpe May 16 '21 at 07:23
  • @Scratch'N'Purr I've already added the python-dotenv in the virtualenv – Davide May 16 '21 at 07:30
  • @jonrsharpe I tried to set the condition `default=TRUE`, but it still doesn't work. Maybe I'm a kind of newbie in Python, how can I check if the condition is True? I'm using VSCode. – Davide May 16 '21 at 07:33
  • Read up on using the debugger, but the simple way is to put `print("loading .env")` inside the condition too. – jonrsharpe May 16 '21 at 07:34
  • Just remove that condition, as pointed by @jonrsharpe it is just a contradiction to check that value you need to read the file but to read the file you need to check that value. Or you can actually set an environment variable for that value, but then that defeats the whole purpose of the package you use. – Abdul Aziz Barkat May 16 '21 at 07:36
  • @jonrsharpe oh, actually you were right, the `READ_DOT_ENV_FILE` goes to false... Do you know why or what am I doing wrong? Thanks! – Davide May 16 '21 at 07:37
  • @AbdulAzizBarkat I think the idea is you'd set that value in the _actual env_, not in the file. – jonrsharpe May 16 '21 at 07:37
  • Well we don't know what value you're actually setting for that env var, so it's hard to say. If you're not setting it: there's your problem. If you're setting it: to what? – jonrsharpe May 16 '21 at 07:38
  • @jonrsharpe I guess I'm not setting (at least, not in the right way). I'll get my previous code from git and try to set the .env again to see if I can find out what I miss. Thanks guys! – Davide May 16 '21 at 07:47

3 Answers3

1

Make sure that your .env file is in the same directory as your settings.py.

Gabriel Rockson
  • 151
  • 1
  • 2
  • Unfortunately, it is. I read about that common mistake, but it's not what my problem is about. – Davide May 16 '21 at 14:03
1

PROBLEM SOLVED

It was easier than I thought. I forgot to type in the terminal export READ_DOT_ENV_FILE=True in order to enable the file reading in development.

Thanks to all who try to help me!

Have a nice day

Davide
  • 435
  • 1
  • 5
  • 12
-1

This may be a hard way

Try following code for every variable in the console


export SECRET_KEY='<SECRET>'