Comments (4)
Hey @bartek! There are two ways to do this: 1) change which class you select or 2) change the class itself.
Like you suggested, the first way would require you to update environment variables. I don't think you would need any fancy import magic; just explicitly declare which environment you're using:
# DJANGO_SETTINGS_MODULE = 'local_settings.py'
# DJANGO_CONFIGURATIONS = 'LocalSettings'
class LocalSettings(SiteSettings, Production):
pass
Since your local settings (presumably) aren't part of your repository, this shouldn't be a problem.
The second option (changing the class definition) is more like the traditional use of local_settings (where the settings module contains different variables depending on the presence of a local_settings module. One simple way to do this is to use a LocalSettings
mixin:
# local_settings.py
class LocalSettings(object):
WHATEVER = 'hello'
# settings.py
try:
from local_settings import LocalSettings
except ImportError:
class LocalSettings: pass
class Production(LocalSettings, SiteSettings):
pass
This way, you wouldn't have to change your environment variables. Cons to this approach are 1) you need to watch out for MRO and 2) depending on the number of settings modules, you may have to mix LocalSettings
into a lot of classes.
Another option would be to use a factory; that is, to point DJANGO_CONFIGURATION
to a function that instantiates the settings object and do your local settings manipulation there (either by directly modifying the instance or by creating a new type that mixes in LocalSettings
and instantiating that). The con to this approach is that you've now moved the logic for selecting your settings class into your code which may or may not be desirable. Of course, you could have the factory method look at yet another environment variable to decide which class to instantiate initially.
Because we're just dealing with plain old classes, there really are a lot of options. (You could also use a meta class to mix in the local settings, for example.) It's hard to say which is ideal.
Because this seems like such a common case, maybe a new feature is in order (that's really a question for @jezdez). I don't know exactly what form it would take—perhaps a function that gets passed the settings module post-load? The downside would be that it would probably require more repeated configuration (at all of the app entry points to specify the "filter function"—though using some kind of convention is also an option), but at least there would be a documented way to do this that didn't require too much headscratching.
from django-configurations.
I have something similar: an app with 4 production sites, 2 test sites, multiple development sites on developer machines, and test and production sites for API (tastypie based). I'm finally happy now that I have a single settings.py using django-configurations.
I achieved this by using a lot of mixins to break the configuration down to a more granular level. So for example, my "site" classes are declared like this:
class Customer1Site(Sentry, PyLibMCCache, ProductionDatabase, ProductionEmail, Base):
SITE_ID = X
COOKIE_DOMAIN = "system.customer1.com"
MEDIA_URL = 'https://media.customer1.com/media/'
STATIC_URL = 'https://media.customer1.com/static/'
class DevSite(NoSentry, DebugMode, DebugToolbar, LocalPyLibMCCache, TestDatabase, DebugEmail, Base):
SITE_ID = X
MEDIA_URL = '/media/'
STATIC_URL = '/static/'
# Production and test sites are all SSL, so this overrides "Base" if the dev uses runserver.
SESSION_COOKIE_SECURE = False
# These also added for runserver
@property
def INSTALLED_APPS(self):
return super(DevSite, self).INSTALLED_APPS + ('django.contrib.staticfiles',)
STATICFILES_DIRS = (
ROOT('app/media/'),
)
class Test1Site(Sentry, DebugMode, DebugToolbar, PyLibMCCache, TestDatabase, DebugEmail, Base):
SITE_ID = X
COOKIE_DOMAIN ="test.customer1.com"
MEDIA_URL = 'https://test-media.customer1.com/media/'
STATIC_URL = 'https://test-media.customer1.com/static/'
SENTRY_DSN = "https://xxxx" # Override production value in Sentry mixin
@property
def CACHES(self):
super(Test1Site, self).CACHES['default']['KEY_PREFIX'] = 'TEST'
return super(Test1Site, self).CACHES
class APISite(NoSentry, PyLibMCCache, ProductionDatabase, ProductionEmail, APISettings, Base):
SITE_ID = X
@property
def CACHES(self):
super(APISite, self).CACHES['default']['KEY_PREFIX'] = 'API'
return super(APISite, self).CACHES
from django-configurations.
Thanks for the replies @matthewwithanm and @vangale!
It's always refreshing to bounce ideas off other brains. My biggest concern was having to either add a bunch of LocalSettings mixins into each settings.sites
setting, or having a large localsettings that declares them all. Using some of these ideas I came up with this very rough approach at the moment:
In settings.sites.mysite
(and every other site module in settings.sites) I've added the following:
from settings.sites import register_configurations_hook
class SiteSettings(object):
# ...
locals().update(register_configurations_hook(SiteSettings))
Then register_configurations_hook
is plainly simple like so:
def register_configurations_hook(settingsClass):
from settings import Development, Production
try:
from settings.localsettings import LocalSettings
except ImportError:
class LocalSettings: pass
class Production(LocalSettings, settingsClass, Production):
pass
class Development(LocalSettings, settingsClass, Development):
pass
return {
'Production': Production,
'Development': Development,
}
This definitely places some of the logic of configuration into the code but I find it fairly sane. I'm not a huge fan of the use of locals() but it's a quick example to get the idea working, I could probably hash it out some more.
Then, my localsettings.py
is simply a plain class that can do whatever. Keeps that part clean.
class LocalSettings(object):
MEDIA_URL = 'http://localhost:8080/media-server/'
# etc.
Thanks again for your contributions. I need to definitely think about this a bit more before I'm happy with a solution but yay for this community! :-)
from django-configurations.
One way of handling "local" settings (per user settings) is the one that I've implemented a while ago for localshop:
import os
from django.core.exceptions import ImproperlyConfigured
from configurations.utils import uppercase_attributes
def FileSettings(path, name=None, required=True):
"""
A simply factory function to return a base class settings
class populated from a Python file located at the given path.
Use it like this::
from configurations import Settings
class Acme(FileSettings('~/.acme/acme.conf.py'), Settings):
pass
You can make reading the file optional by passing in a ``required``
parameter::
from configurations import Settings
local_settings = '~/.acme/acme.conf.py'
class Acme(FileSettings(local_settings, required=False), Settings):
pass
"""
if name is None:
name = __name__
path = os.path.expanduser(path)
mod = imp.new_module('%s.local' % name)
mod.__file__ = path
class Holder(object):
pass
try:
execfile(path, mod.__dict__)
except IOError, e:
if required:
raise ImproperlyConfigured(
"Notice: Unable to load required configuration file %s (%s), "
"using default settings\n\n" % (path, e.strerror))
return Holder
for name, value in uppercase_attributes(mod).items():
setattr(Holder, name, value)
return Holder
I'm not sure which strategy is best though, I'd rather not make that decision for developers.
from django-configurations.
Related Issues (20)
- Improve Dotenv usage to avoid basic issues HOT 1
- Use the 'furo' theme for Sphinx documentation
- Improve code blocks in documentation
- Release minor version 2.4.1 HOT 3
- 2.4.1: pytest is failing HOT 2
- Replace deprecated imp module with importlib HOT 4
- How can this be used with `django.setup()` HOT 2
- Make magical importing optional
- <frozen importlib._bootstrap>:1049: ImportWarning: ConfigurationImporter.find_spec() not found; falling back to find_module() HOT 4
- "STATICFILES_STORAGE/STORAGES are mutually exclusive" on Django>=4.2
- WSGI application loaded twice unexpectedly
- Release 2.4.2 is not on PyPI HOT 9
- Update Python and Django supported versions
- Add support for Python 3.12 HOT 1
- DeprecationWarning for USE_DEPRECATED_PYTZ, CSRF_COOKIE_MASKED, USE_L10N
- Broken link in `Configuration.load_dotenv` docstring HOT 1
- Add Django 5.0 classifier
- Publish version 2.5.1 on PyPi HOT 2
- Values not working in DATABASES dictionary and values.Value returning Value instance type instead of a str HOT 1
- DOTENV - Using environ_name strange behevior HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from django-configurations.