Package: django-classbasedsettings
This project allows you to define your Django project’s settings using classes
instead of modules. Among other things, this allows you to use inheritance and
calculated properties.InstallationThe easiest way to install is by using pip:pipinstalldjango-classbasedsettingsHowever you can also just drop the “cbsettings” folder into your pythonpath.SetupThe places where you’re currently settingDJANGO_SETTINGS_MODULE, you’ll have
to instead callcbsettings.configure. So your manage.py will look something
like this:#!/usr/bin/env pythonimportosimportsysimportcbsettingsif__name__=="__main__":os.environ.setdefault('DJANGO_SETTINGS_FACTORY','path.to.MySettings')cbsettings.configure()fromdjango.core.managementimportexecute_from_command_lineexecute_from_command_line(sys.argv)You’ll have to make a similar modification to your wsgi file:importosimportcbsettingsfromdjango.core.wsgiimportget_wsgi_applicationos.environ.setdefault('DJANGO_SETTINGS_FACTORY','path.to.MySettings')cbsettings.configure()application=get_wsgi_application()UsageBasicThe only real change you need to make to the settings.py file that Django
creates for you is to nest all the variables in a class:fromcbsettingsimportDjangoDefaultsclassMySettings(DjangoDefaults):ADMINS=(# ('Your Name', 'your_email@example.com'),)MANAGERS=ADMINSDATABASES={'default':{'ENGINE':'django.db.backends.','NAME':'','USER':'','PASSWORD':'','HOST':'','PORT':'',}}# etc, etcNotice that the class extendsDjangoDefaults. By inheriting from this class,
you get all the default settings values that Django normally composites your
settings with. (These are pulled in fromdjango.conf.global_settingsso
they’ll track with your version of Django, not classbasedsettings.) You can
also do stuff like this:classMySettings(DjangoDefaults):STATICFILES_FINDERS=DjangoDefaults.STATICFILES_FINDERS+('my.custom.StaticFileFinder',)# etcThese are just normal Python classes, so you can do anything you normally can:classMySettings(DjangoDefaults):@propertydefTEMPLATE_DEBUG(self):# Now a subclass can override DEBUG and TEMPLATE_DEBUG will be changed accordinglyreturnself.DEBUG# etcCallable properties are automatically called:classMySettings(DjangoDefaults):TEMPLATE_DEBUG=lambdas:s.DEBUG…unless you don’t want them to be:fromcbsettingsimportcallable_settingclassMySettings(DjangoDefaults):@callable_settingdefSOME_SETTING(self,*args,**kwargs):# This setting is actually a callable. The decorator tells cbsettings# not to invoke it to get a settings value....# You can also use the decorator with functions defined elsewhereSOME_OTHER_SETTING=callable_setting(my_function)You can also prevent your callable settings from receiving a “self” argument:fromcbsettingsimportcallable_settingclassMySettings(DjangoDefaults):@callable_setting(takes_self=False)defSOME_SETTING(*args,**kwargs):...SOME_OTHER_SETTING=callable_setting(takes_self=False)(my_function)Per-App MixinsTwo classes are provided to save you from having to type out long setting names:PrefixedSettingsandAppsettings. These are meant for declaring subsets
of your settings which share a prefix. The classes can then be mixed into your
real settings class.PrefixedSettingswill apply an arbitrary prefix, which can be provided via
a Meta class. If none is specified, it will extract the prefix from the class
name:fromcbsettingsimportPrefixedSettingsclassMyFancySettings(PrefixedSettings):VALUE=5The above will result in a setting namedMY_FANCY_VALUE. (You would get the
same result by naming the classMyFancy—without the “Settings” suffix.) If a
prefix is specified, it will be used without manipulation. In other word:classMyFancySettings(PrefixedSettings):VALUE=5classMeta:prefix='hello'will result in a setting namedhelloVALUE.AppSettingsis similar, but it uses a different Meta attribute and does a
little extra formatting. In most cases, you’ll want to useAppSettingsand
notPrefixedSettings:fromcbsettingsimportAppSettingsclassMyAppSettings(AppSettings):VALUE=5will result in a setting namedMY_APP_VALUE. (You would get the same result
by naming the classMyApp—without the “Settings” suffix.) If an app name is
provided explicitly, it will be uppercased and an underscore will be appended:classMyAppSettings(AppSettings):VALUE=5classMeta:app_name='somebody_elses_app'will result in a setting namedSOMEBODY_ELSES_APP_VALUE.Using a Settings FactoryYou might be thinking that hardcoding your settings class into files is just as
bad as Django’s hardcoding of the settings module. That’s true. Which is whyconfigure()can be passed the path to any callable that returns a settings
object instance. So your manage.py might instead look like this:#!/usr/bin/env pythonimportsysimportcbsettingsif__name__=="__main__":cbsettings.configure('path.to.my.settings.factory')fromdjango.core.managementimportexecute_from_command_lineexecute_from_command_line(sys.argv)Then, inpath/to/my/settings.py:deffactory():if'DEV'inos.environ:returnMyDebugSettings()else:returnMyProductionSettings()Now you can easily change which settings class you’re using based on whatever
conditions you want without having to make modifications to multiple files.Using SwitcherUsing a factory method to determine which settings class to use is a powerful
feature! But usually you’ll want to switch settings classes based on the same
kinds of conditions, so django-classbasedsettings comes with a factory that’ll
handle these common cases, and allow you to easily define simple conditions of
your own. It also uses a more declarative syntax, which makes it more organized
than a factory method. Here’s how you use it in your settings file:fromcbsettingsimportDjangoDefaults,switcherclassMyProductionSettings(DjangoDefaults):DEBUG=False# etcclassMyDevSettings(DjangoDefaults):DEBUG=True# etcclassMyTestingSettings(MyProductionSettings):SOME_VAR='whatever'# You can use one of the preregistered conditions by passing kwargs. The# first class whose conditions are all met will be used.switcher.register(MyTestSettings,testing=True)switcher.register(MyDevSettings,hostnames=['mycompuer.home','billscomputer.home'])switcher.register(MyProductionSettings,hostnames=['theserver.com'])# ...or you can define your own simple checks as positional arguments. If# all of the values are truthy (and any kwarg checks pass), the class will# be used.switcher.register(MyDevSettings,'dev.mysite.com'in__file__)switcher.register(MyDevSettings,os.environ.get('DEV'))# Callable positional arguments will be called, then checked for truthiness.switcher.register(MyDevSettings,lambda:randint(1,2)==2)You can also useswitcher.registeras a class decorator:@switcher.register(hostnames=['theserver.com'])classMyProductionSettings(DjangoDefaults):DEBUG=False# etcThen, wherever you’re callingconfigure, pass it your module’sswitchervariable:cbsettings.configure('path.to.my.settings.switcher')