Set DJANGO_SETTINGS_MODULE through uwsgi - django

I am trying to find the best way to split my django settings, and have the different settings files served for dev and prod.
My wsgi file:
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
and in my uwsgi.ini file I have this:
env = DJANGO_SETTINGS_MODULE=project.settings.local
wsgi-file = project/wsgi.py
But the env from the uwsgi.ini file is not passed to wsgi. If I print os.environ in the wsgi file the DJANGO_SETTINGS_MODULE is not set.
Any ideas?

I like doing something like this in wsgi.py:
import os
import socket
socket_name = socket.gethostname()
if "stage" in socket_name:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.staging')
elif "prod" in socket_name:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')
else:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.local')
application = get_wsgi_application()
Your method of detecting whether you're on staging, prod, or local can be whatever you'd like, but the os.environ.setdefault command seems like it'll accomplish what you're hoping to do.

My solution was to also set the DJANGO_SETTINGS_MODULE in the bin/activate file. I have added it after export PATH like so:
export PATH
export DJANGO_SETTINGS_MODULE='project.settings.local'
You should find your bin folder in your project environment folder.

Related

Django os.getenv('SECRET_KEY') throwing "The SECRET_KEY setting must not be empty."

I'm setting up Django using os.getenv to prepare it for deploying using Docker but it seems it is not reading the .env file. Any idea why is not reading it?
Here is the setup:
.env
SECRET_KEY=foo
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
settings.py abstraction
import os
from pathlib import Path
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = os.getenv('SECRET_KEY')
DEBUG = os.getenv('DEBUG')
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS')
You can use python-decouple to get the environment variable stored in the root of your project in a .env file.
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
EMAIL_HOST = config('EMAIL_HOST', default='localhost')
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)
Note: for changes to apply you need to restart the server.
I'm using python-dotenv in order to implement dotenv functionality. If you want Django to find your .env file, you need to modify manage.py and wsgi.py files.
# manage.py
import os
import sys
import dotenv
def main():
"""Run administrative tasks."""
# dotenv settings
dotenv.load_dotenv(
os.path.join(os.path.dirname(__file__), '.env')
)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
if os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
# wsgi.py
import os
import dotenv
from django.core.wsgi import get_wsgi_application
# dotenv settings
dotenv.load_dotenv(
os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
if os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
application = get_wsgi_application()
When deploying via docker-compose, you can specify in the docker-compose file.bml in the container settings [web]:
web:
...
env_file:
- ./.env
It worked for me without using additional packages. In my case , the file .env is located in the directory where docker-compose.yml is located
To get environment variables from docker or from the AWS Elastic Beanstalk i use
os.environ.get('SECRET_KEY'), this is generally more reliable than os.environ['SECRET_KEY']

How to specify environment variables in Apache site config for Django?

How do you specify environment variables for an Apache site?
I have a multi-tenant Django site being served by Apache+ModWSGI. Each site uses the same Django code, but their settings differ slightly. An environment variable tells Django which settings to load. Currently, I'm getting a separate wsgi_<site_name>.py file, containing the variable appropriate for each site, but this violate the DRY rule. I'd like to, instead, put this variable in the Apache site config, and use a single wsgi.py file for all sites.
I can't find much info on how to do this. I found this one old blog post suggesting I could use the Apache config syntax:
SetEnv SITE somename
along with a wsgi file like:
import os, sys
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.abspath(os.path.join(BASE_DIR, '..'))
sys.path.append(PROJECT_DIR)
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'
from django.core.wsgi import get_wsgi_application
_application = get_wsgi_application()
def application(environ, start_response):
os.environ['SITE'] = environ['SITE']
return _application(environ, start_response)
but this has no effect. If I print out the SITE variable, Django never receives the value "somename" and displays the default value.
As far as I can tell the mod_env component is still supported, and I've confirmed it's both installed and enabled on my server.
The problem was that I was calling get_wsgi_application() too early. The correct code is:
from django.core.wsgi import get_wsgi_application
def application(environ, start_response):
os.environ['SITE'] = environ['SITE']
_application = get_wsgi_application()
return _application(environ, start_response)

Declare Django settings to use with mod_wsgi/apache

I have multiple settings files in my Django project (base.py, local.py, production.py).
Locally, I'm kicking off the Django dev server via
python manage.py runserver --settings=config.settings.local
How can I tell mod_wsgi to use the same file? I'm testing this on my local apache instance, but will need to figure out the same thing for my production apache instance.
--settings=config.settings.production
Current httpd-vhosts.conf:
WSGIDaemonProcess secureDash python-path=/Users/user/projects/secureDash_project/secureDash python-home=/Users/user/.venvs/securedash_py3.6
WSGIProcessGroup secureDash
WSGIScriptAlias / /Users/user/projects/secureDash_project/config/wsgi.py
Error that I see in the apache log:
ModuleNotFoundError: No module named 'secureDash.settings'
Django 1.11
mod_wsgi-4.5.15
Apache 2.4.18
You do this by setting the DJANGO_SETTINGS_MODULE environment variable. You can either do this in your Apache vhost configuration, or in the wsgi file itself.
I solved this by adding DJANGO_SETTINGS_MODULE to my secrets.json file in each environment.
secrets.json:
{
"FILENAME": "secrets.json",
"SECRET_KEY": "someSuperSecretiveSecret!",
"DJANGO_SETTINGS_MODULE": "config.settings.local"
}
wsgi.py:
import json
from django.core.exceptions import ImproperlyConfigured
from pathlib import Path
...
# Directory Paths
BASE_DIR = Path(__file__).resolve().parent
# JSON-based secrets module
SECRETS_JSON = str(BASE_DIR) + '/secrets.json'
with open(SECRETS_JSON) as f:
secrets = json.loads(f.read())
def get_secret(setting, secrets=secrets):
'''Get the secret variable or return explicit exception'''
try:
return secrets[setting]
except KeyError:
error_msg = 'Set the {0} environment variable'.format(setting)
raise ImproperlyConfigured(error_msg)
DJANGO_SETTINGS_MODULE = get_secret('DJANGO_SETTINGS_MODULE')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", DJANGO_SETTINGS_MODULE)

IOError: [Errno 2] No such file or directory: '/var/www/.virtualenvs/exampleenv/bin/activate_this.py")

I have use virtualenv for deploying a django app on a vps. The following is the content of wsgi file placed in /var/www/example.wsgi
import os
import sys
import site
# Add the site-packages of the chosen virtualenv to work with
site.addsitedir('~/.virtualenvs/exampleenv/local/lib/python2.7/site-packages')
# Add the app's directory to the PYTHONPATH
sys.path.append('/var/www/example')
sys.path.append('/var/www/example/example')
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
# Activate your virtual env
activate_env=os.path.expanduser("~/virtualenvs/exampleenv/bin/activate_this.py")
execfile(activate_env, dict(__file__=activate_env))
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
But the error.log file shows the following error.
IOError: [Errno 2] No such file or directory: '/var/www/.virtualenvs/exampleenv/bin/activate_this.py")
It is looking for virtualenv inside /var/www instead of ~/.virtualenv/.....
I have already checked the path of the exampleenv which exists in ~/.virtualenvs/exampleenv
The app is probably running as different user (which home dir is /var/www/), so expanduser function will use its home dir not yours.
It is not a good practice to put others users files in your home dir. Try to put virtualenv in lets say /var/www/<your_app>/.venv or /var/www/.<your_app>_venv.

Set up the Django settings file via DJANGO_SETTINGS_MODULE environment variable with Apache WSGI

How to change the settings file used by Django when launched by Apache WSGI only with DJANGO_SETTINGS_MODULE environment variable ?
The Django documentation shows how-to achieve this with a different WSGI application file but if we don't want to create a dedicated WSGI file as well as a dedicated settings file for our different environments, using only environment variable DJANGO_SETTINGS_MODULE in Apache with SetEnv is not sufficent.
The variable is indeed passed to application call in environ variable but as the django.conf retrieve the settings like this :
settings_module = os.environ[ENVIRONMENT_VARIABLE]
it never see the right variable.
As only one instance of a Django application can be run within the context of a Python sub interpreter, the best way of doing things with mod_wsgi would be to dedicate each distinct Django site requiring a different settings, to a different mod_wsgi daemon process group.
In doing that, use a name for the mod_wsgi daemon process group which reflects the name of the settings file which should be used and then at global scope within the WSGI script file, set DJANGO_SETTINGS_MODULE based on the name of the mod_wsgi daemon process group.
The Apache configuration would therefore have something like:
WSGIDaemonProcess mysite.settings_1
WSGIDaemonProcess mysite.settings_2
WSGIScriptAlias /suburl process-group=mysite.settings_2 application-group=%{GLOBAL}
WSGIScriptAlias / process-group=mysite.settings_1 application-group=%{GLOBAL}
and the WSGI script file:
import os
try:
from mod_wsgi import process_group
except ImportError:
settings_module = 'mysite.settings'
else:
settings_module = process_group
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
To achieve that, you can use the following code in your application's wsgi.py file :
import os
from django.core.wsgi import get_wsgi_application
def application(environ, start_response):
_application = get_wsgi_application()
os.environ['DJANGO_SETTINGS_MODULE'] = environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')
return _application(environ, start_response)
Tip: don't forget to customize the myapp.settings string to match your default settings module.