Package: django-stagesetting
An application for managing site configuration through normalDjangoforms,
and thus through theadmin site.ReleaseStatusstable (0.5.0)masterPre-requisitesThe following versions are tested:Python 2.7, or 3.3+Django1.7+InstallationFirst up, you need to install it  (viapipas usual):pip install django-stagesetting==0.5.0Once that’s downloaded, add the package to yourINSTALLED_APPSin your settings:INSTALLED_APPS = (
    # ...
    'stagesetting',
    # ...
)do a migrate:python manage.py migrate stagesettingAdd aSTAGESETTINGSdictionary to your project’s settings:STAGESETTINGS = {
    'SETTING_NAME': '...',
    'ANOTHER_SETTING_NAME': '...',
}The setting collection name is the dictionary key, so must be unique.Writing settingsSettings may be created in a number of ways, the simplest of which is to
provide adictionaryas the value:STAGESETTINGS = {
    'MY_SETTING': {
        'an_example-datetime': datetime.today(),
        'a_date': date.today(),
        'time_now': time(4, 23),
        'boolean_field': False,
        'plain_text': 'char field',
        'decimal': Decimal('3.25'),
        'float': 2.3,
    }
}where possible, this will auto-generate a Form class for you, choosing sensible
defaults for the field variants where possible.The other option is for the value to be alistor atuple, where
thefirst itemrepresents a form (either adictionaryas above,ORthedotted.path.to.a.Form.Classif you need custom validation) and thesecond, optional itemis the default data. The following should all be valid:STAGESETTINGS = {
    'AUTO_GENERATED': [{
        'datetime': datetime.today(),
    }],
    'IMPORT_A_FORM': ['myapp.forms.MyForm'],
    'IMPORT_WITH_DEFAULT': ['myapp.forms.MyForm', {'default': 'data'}],
    'AUTO_GENERATED_WITH_OTHER_DEFAULTS': [{
        'datetime': datetime.today(),
    }, {'default': 'data'}],
}A simple configuration form (for thedotted.path.Format) might look like:from django.core.exceptions import ValidationError
from django.forms import Form, DateField

class DateForm(Form):
    start = DateField()
    end = DateField()

    def clean(self):
        cd = self.cleaned_data
        if 'start' in cd and 'end' in cd and cd['start'] > cd['end']:
            raise ValidationError("Start date cannot be after end date")
        return cdAs you can see, it really is just a normalForm. Internally, this form’scleaned_datawill be converted intoJSONbefore being saved to the
database.
It will get re-converted to proper Python values when pulled out
of the database, by going through the givenFormclass’s validation again,
including converting to rich values like model instances.Python types which can be detectedWhen detecting a dictionary as the value and auto-generating a form, the
following translations will be applied:NonebecomesNullBooleanFielddatetime.datetimebecomesDateTimeFielddatetime.datebecomesDateFielddatetime.timebecomesTimeFielddatetime.timedeltabecomesDurationFielddecimal.DecimalbecomesDecimalFieldfloatbecomesFloatFieldTrueorFalsebecomeBooleanFieldintbecomesIntegerFielduuid.UUIDbecomesUUIDFieldorRegexField, depending on theDjangoversionlistandtuplebecomeMultipleChoiceFieldcollections.OrderedDict,set,frozenset, anddictbecomeChoiceFieldmodels.Modelinstances becomeModelChoiceFieldmodels.QuerySetbecomesModelMultipleChoiceFieldstrings become one of the following, depending on what checks they pass:GenericIPAddressFieldURLFieldEmailFieldSlugFieldCharFieldIntegerFieldDecimalFieldUUIDField(orRegexField, depending on theDjangoversion)DateTimeFieldDateFieldTimeFieldSome strings arereallyspecial, and will instead turn into one of the following:if the string ==STATIC_URLorSTATICFILES_STORAGEthe field will be
aChoiceFieldwhose options are all the files found by the
project’sSTATICFILES_FINDERS.if the string ==MEDIA_URL``or``DEFAULT_FILE_STORAGEthe field will be
aChoiceFieldwhose options are all the files found byDEFAULT_FILE_STORAGE.if the stringstarts withSTATIC_URLit will be the same as using
theSTATIC_URLgenerated field, but is a regular expression for filtering
to only certain files (i.e./static/.*\.css$would find only css files)if the stringstarts withMEDIA_URLit will be the same as above,
but for files found inDEFAULT_FILE_STORAGE.if a string looks like it contains HTML, it will try to usedjango-bleachfor sanitisation, and one ofdjango-ckeditor,django-tinymce,django-markdown,django-pagedown, ordjango-epiceditorfor an
appropriate widget.Usage in codeThe best way to access the settings in your views is to includestagesetting.middleware.ApplyRuntimeSettingsin yourMIDDLEWARE_CLASSESwhich will ensure there is arequest.stagesettingsvariable which can be
used like so:def myview(request):
    how_many_form_data = request.stagesetting.LIST_PER_PAGE
    allow_empty_form_data = request.stagesetting['ALLOW_EMPTY']each setting will be a dictionary of theFormvalues, either the default ones
or those changed in the database.Usage in templatesIf you’ve already gotrequestin your template, obviously you can continue
to userequest.stagesettingsif the middleware is wired up.If you don’t have request, or you’re not using the middleware,stagesetting.context_processors.runtime_settingsprovides aSTAGESETTINGtemplate variable which contains the exact same data.Finally, if not using the middleware nor the context processor, there is a
template tag available as a last resort. It’s usage is:{% load stagesetting %}
{% stagesetting as NEW_CONTEXT_VARIABLE %}
{{ NEW_CONTEXT_VARIABLE.SETTING_NAME.fieldname }}Usage outside of a requestIf you don’t have the middleware, or are in a part of the code which doesn’t
have arequest, you can use the wrapper object directly:from stagesetting.models import RuntimeSettingWrapper
def my_signal_handler(sender, instance, **kwargs):
    live_settings = RuntimeSettingWrapper()
    data = live_settings.LIST_PER_PAGETry to keep a singleRuntimeSettingWrapperaround for as long as possible,
rather than creating a new instance everywhere, as the object must fetch
the available settings from the database the first time it needs them. It
caches them for it’s lifetime thereafter.AlternativesOther apps I know of that achieve similar things, or overlap in some obvious
way. I won’t judge you for using them, and I can’t promise this is better.
To the victor, the spoils of maintenance!django-constanceis similarusespickleto store an arbitrary python value;stagesettingonly
stores stuff it can put into JSON and relies onDjangoFormsto inflate
the JSON back into python values.Has both database and redis backends;stagesettingonly supports
the database, though it will only do one query most of the time.django-dynamic-preferencesby the look of it.django-soloas well.If you think GitHub popularity is an indication of usage and battle-tested
production-readiness, then any of the above are certainly worth considering,
being much more noticed than this, my attempt.Licensedjango-stagesetting0.5.0is available under the terms of the
Simplified BSD License (alternatively known as the FreeBSD License, or
the 2-clause License):Copyright (c) 2015, Keryn Knight
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.Change log0.5.0Minor changes to allow for easy subclassing or replacement of the middleware,
context processor etc.Updated test matrix to cover more versions of Django (1.9+)0.4.2Admin now displays pretty names for the setting keys.StaticFilesChoiceFieldandDefaultStorageFilesChoiceFieldnow both use an
LRU cache to avoid having to enumerate the storage backends/finders repeatedly.0.4.1When generating a form from anOrderedDict, field ordering is now preserved.When a form’s values are saved into the database and become stale (because the
underlying form/defaults are added to), the new defaults are transparently
mapped onto the value when it’s made available via the middleware,
context processor or template tag.0.4.0Introduced thestagesettingtemplate tag.Settings are no longer automatically created on appready(), instead
the defaults are lazily converted when requested via aRuntimeSettingWrapperAddedStaticFilesChoiceFieldandDefaultStorageFilesChoiceField,
which allow for selecting a file that already exists in the given storage.When auto-generating a form, theSTATIC_URLandMEDIA_URLsettings
are treated as special, and turned into the aforementioned fields.Providing an incomplete defaults dictionary in the second parameter of a
given setting’s config will now showInfomessages to indicate what’s
missing.When auto-generating a form from a dictionary, HTML-like values will try
to use one ofdjango-ckeditor,django-tinymce,django-markdown,django-pagedown, ordjango-epiceditorfor a widget instead of a normaltextarea.Added support for usingdjango-bleachwhen encountering HTML-like values
in an autogenerated form.Added initial support fordjangorestframework0.3.2Fixed error introduced in0.3.1when only providing a dictionary, because
it turns out it wasn’t being covered by tests.0.3.1Fixed issue where providing a dictionary wasn’t treating the values as
implicit defaults to be created into the database.0.3.0Added ability to auto-generate forms for configuration values which are
dictionaries.0.2.0Initial release.