I want to have different settings.py for when I run my project locally and in production, because I have a setting in settings.py that forces my project to use https, but django runserver.py can only use http, not https, so I am unable to run my project locally now. Also, I want to have debug=True on my computer, and debug=False in production. So how can I have a different settings.py for my computer and for production?
There are some options, one these to create two settings files:
add the following to the end of your settings.py:
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
PROJECT_APP_PATH = os.path.dirname(os.path.abspath(__file__))
PROJECT_APP = os.path.basename(PROJECT_APP_PATH)
BASE_DIR = os.path.dirname(PROJECT_APP_PATH)
DEBUG = False
...
...
##################
# LOCAL SETTINGS #
##################
# Allow any settings to be defined in local_settings.py which should be
# ignored in your version control system allowing for settings to be
# defined per machine.
f = os.path.join(PROJECT_APP_PATH, "local_settings.py")
if os.path.exists(f):
import sys
import imp
module_name = "%s.local_settings" % PROJECT_APP
module = imp.new_module(module_name)
module.__file__ = f
sys.modules[module_name] = module
exec(open(f, "rb").read())
Create local_settings.py and overwrite the desired settings:
#####################
# OVERWRITE SETTINGS #
#####################
DEBUG = True
SECURE_PROXY_SSL_HEADER = None
SECURE_SSL_REDIRECT = False
...
You could have a folder called settings that include different python files for the base settings, the dev settings, the staging settings, the production settings and the test settings.
The base settings could be imported in dev, staging, production and test settings and then, you can set the value of existing variables (e.g. DEBUG) or define new variables.
The tree structure could be:
{your-project-name}/
settings/
base.py
dev.py
staging.py
production.py
test.py
Then you can use them such as:
$ python manage.py runserver --settings={your-project-name}.settings.dev
For the deployment, you could pass the proper settings through env variable (e.g. in case of docker images)
Related
django-project/
migrations/
app1/
.../
src/
app1/
.../
config/
...
settings.py
how to set the path in MIGRATION_MODULES in settings.py to make generated migration files appear in migrations/app1/ folder?
I have tried
MIGRATION_MODULES = {'app1': '..migrations.app1.db_migrations'}
but got errors.
Is not it a bad practice to move migration files from default location?
To move the migration files outside of your Django project, you can do the following:
Make a new directory outside of your Django project to store the
migrations files. In your Django project's settings.py file, set the
MIGRATION_MODULES setting to a dictionary that maps the app labels
to the new directory that you created. For example:
MIGRATION_MODULES = {
'app_name': 'path.to.migration.module',
}
Move the migrations files for each app to the new directory.
Run the makemigrations and migrate management commands as usual.
Here is how to do it.
In settings.py you are going to add these lines:
import os, sys
# Path to django-project/ from __file__ or settings.py
proj_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
# Path to the migration folder
new_mig_folder = os.path.join(proj_dir, 'migrations/')
# And to add the folder to the path
sys.path.append(new_mig_folder)
# Hurrah when you execute the code it will know to look for files in this folder
# Tell Django where the migration files are if not default location
MIGRATION_MODULES = {
'app1': 'app1.folder', # If in subfolder
'app2': 'app2_mig', # If files directly in migrations/app2_mig/
'app3': 'whatever_you_want_to_call_it',
}
I have this standard django generated settings:
project/project
| __init__.py
| settings.py
| urls.py
| wsgi.py
And it's working. However, I want to reorganize this layout to this:
project/project/settings
| __init__.py
| base.py
| __init__.py
| urls.py
| wsgi.py
When I do it of course it's not working:
django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty.
So where and what I need to change in order this to work?
Well according to your error which says, you have no SECRET_KEY defined. You need to add one to your settings.py.
Django will refuse to start if SECRET_KEY is not set. You can read more about it in docs
The SECRET_KEY can be anything...but if you want to use Django to generate one, you can do the following from the python shell:
>>> from django.utils.crypto import get_random_string
>>> chars = 'abcdefghijklmnopqrstuvwxyz0123456789!##$%^&*(-_=+)'
>>> SECRET_KEY = get_random_string(50, chars)
>>> print SECRET_KEY
Copy the secret_key to your setting file.
Hope this will help you! :)
It's very simple, just edit manage.py:
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
to
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.base")
and also wsgi.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
to
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.base")
If you moved your old settings.py into settings/base.py, I assume you want to split your Django settings into multiple files.
So, the only thing you have to do is to ensure all your settings are available at module level. In settings/__init__.py, write something like
from .base import *
from .another_setting_file import *
# etc.
Then, all your settings variables (settings.SECRET_KEY, settings.DEBUG, etc) will be available to the rest of the Django app, without need to modify default DJANGO_SETTINGS_MODULE value.
Bonus: using this method, you can override a setting defined in base.py in another file as soon as you import base first in __init__.py
Django is looking into DJANGO_SETTINGS_MODULE env variable by default. It's set to
'{your_project_name}.settings' module or package (again, by default). So you can modify that to be '{your_project_name}.settings.base' to read settings from base.py module.
But you can also accomplish that without modifying environment variables. With your folder structure Django is looking into settings package you've created. So, you just need to import all the settings defined in base.py there. Just place this code into settings/__init__.py module:
from .base import *
This allows you to import settings from different modules and perform some more complex logic like checking the environment variables and importing settings based on your current environment. For example importing settings by checking in which environment the app is running:
import logging
import os
# Default is local environment
environment = os.getenv('MACHINE_ENV', 'local')
if environment:
if environment.lower() == 'local':
from .local import *
elif environment.lower() == 'staging':
from .staging import *
elif environment.lower() == 'prod':
from .prod import *
It seems to be relatively normal in the Django world to import a local environment settings override file at the end of settings.py, something like:
from settings_local.py import *
This has a couple of issues, most notably for me being that you cannot inspect or modify base settings, only override them - which leads to some painful duplication and headaches over time.
Instead, is there anything wrong with importing an init function from a local file and injecting the output of globals() to allow that local file to read and modify the settings a bit more intelligently?
settings.py:
from settings_local.py import init
init( globals() )
settings_local.py:
def init( config ):
config['INSTALLED_APPS'].append( 'some.app' )
This seems to address a number of the concerns related to using import *, but I'd like to know if this is a bad idea for a reason I am not yet aware of.
The more common and readable way to handle this is to have multiple settings files, where you put all the common settings in one module and import that at the beginning of the environment-specific settings files:
settings
__init__.py
base.py
development.py
staging.py
production.py
This allows you the mutations you are referring to:
# base.py
INSTALLED_APPS = [
'foo',
'bar'
]
# development.py
from .base import *
INSTALLED_APPS += [
'debug_toolbar'
]
Then you run your app locally with DJANGO_SETTINGS_MODULE=settings.development
I'm developing a Django website and I have a media folder where users can upload some stuff. This folder is in my root folder and when I run the server in local (with the ./manage.py runserver) it works fine and put the files in MyApp/media/
The problem is that I have a production server Apache with the website running via mod_wsgi. The folder of my project is in /var/www/MyApp/ and it is creating my media folder in /var/www/media instead of /var/www/MyApp/media.
In my settings I have
STATIC_URL = 'static/'
MEDIA_URL = 'media/'
And the way I'm creating the path for my uploaded files is this:
def generate_path(self, filename):
url = "media/files/users/%s/%s" % (self.user.username, filename)
return url
Any idea of what in production it is changing the directory?
Thanks in advance
Configure your MEDIA_ROOT:
# Project root is intended to be used when building paths,
# e.g. ``os.path.join(PROJECT_ROOT, 'relative/path')``.
PROJECT_ROOT = os.path.abspath(os.path.dirname(__name__))
# Absolute path to the directory that will hold uploaded files.
#
# For more information on ``MEDIA_ROOT``, visit
# https://docs.djangoproject.com/en/1.8/ref/settings/#media-root
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'uploads/')
Then, use upload_to to specify path relative to your MEDIA_ROOT.
I have created a production_settings.py in which I am putting all the production env variables and values eg:
import dj_database_url
DATABASES['default'] = dj_database_url.config()
I thought I'd declare an env variable like
MYPROJECT_PRODUCTION
and set this like
heroku config:add MYPROJECT_PRODUCTION=True or export MYPROJECT_PRODUCTION=True
In the settings.py (which is the default created by django) ,I thought I'd add at the end of the file
import os
if os.environ.has_key('MYPROJECT_PRODUCTION') and os.environ.get('MYPROJECT_PRODUCTION')=='True':
from production_settings import *
Is this the correct way of doing this?
I am getting an import error when I try python manage shell
export DJANGO_SETTINGS_MODULE='myproject.settings'
export MYPROJECT_PRODUCTION=True
me#ubuntu:~/dev/python/django/myproject$ python manage.py shell
Error: Can't find the file 'settings.py' in the directory containing 'manage.py'. It appears you've customized things.
You'll have to run django-admin.py, passing it your settings module.
(If the file settings.py does indeed exist, it's causing an ImportError somehow.)
the manage.py exists in the same folder as settings.py ..still the error occurs.
I checked echo $MYPROJECT_PRODUCTION which outputs True
Personally, I keep my production settings in settings.py then include a local_settings.py file (that's excluded from revision control with .hgignore).
I add the following to the end of settings.py
try:
from local_settings import *
except ImportError, e:
pass
Then in my local_settings.py I override the appropriate settings -
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'tag',
'USER': 'tag',
'PASSWORD': 'tag',
'HOST': 'localhost',
'PORT': '5432',
}
}
I'm under the impression that this is a fairly well used method (I picked it up from colleagues but I've seen it blogged about too)
EDIT
In response to balazs very good point you might include a variation on this method, in order to keep sensitive data private. Perhaps include the following after the local_settings import -
try:
from production_settings import *
except ImportError, e:
pass
Then exclude production_settings.py from version control. I guess you may need to use a different method to deploy production_settings.py, but I don't suppose that's too big a deal.
I advise against using different settings files for different environments in favour of customising your settings with environment variables. This allows you by default to use your local development settings and override it in production.
For example database and static / media root settings
# Default database URL is a local SQLite instance
DATABASE_URL = 'sqlite:///%s' % os.path.join(os.path.dirname(__file__), 'db.sqlite')
DATABASES = {
'default': dj_database_url.config('DATABASE_URL', default=DATABASE_URL),
}
MEDIA_ROOT = os.environ.get('MEDIA_ROOT',
os.path.join(os.path.dirname(__file__), 'media'))
MEDIA_URL = os.environ.get('MEDIA_URL', '/media/')
STATIC_ROOT = os.environ.get('STATIC_ROOT',
os.path.join(os.path.dirname(__file__), 'static'))
STATIC_URL = os.environ.get('STATIC_URL', '/static/')
This allows you to set any setting on Heroku via heroku config:set and removes the complexity of dealing with multiple settings files, for example:
heroku config:set MEDIA_URL=http://custom-media-server.com/media/
heroku config:set STATIC_URL=http://custom-media-server.com/static/
I have also created a custom Django project template that can take most settings from environment variables.
You should also look at the 12 Factor Application website and specifically at how to store configuration.
Have you defined DATABASES as a dictionary? by:
DATABASES = {}
also show your heroku logs