https://github.com/kezabelle/django-stagesetting
dynamic site settings, based on Django Forms, and optionally the Django Admin
https://github.com/kezabelle/django-stagesetting
django django-admin django-application django-middleware django-settings
Last synced: 3 months ago
JSON representation
dynamic site settings, based on Django Forms, and optionally the Django Admin
- Host: GitHub
- URL: https://github.com/kezabelle/django-stagesetting
- Owner: kezabelle
- License: other
- Created: 2015-07-21T20:31:46.000Z (almost 10 years ago)
- Default Branch: master
- Last Pushed: 2018-09-14T11:00:05.000Z (over 6 years ago)
- Last Synced: 2025-02-01T00:23:33.849Z (3 months ago)
- Topics: django, django-admin, django-application, django-middleware, django-settings
- Language: Python
- Homepage:
- Size: 123 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.rst
- Changelog: CHANGELOG
- License: LICENSE
Awesome Lists containing this project
README
django-stagesetting 0.5.0
=========================An application for managing site configuration through normal `Django`_ forms,
and thus through the `admin site`_... |travis_stable| image:: https://travis-ci.org/kezabelle/django-stagesetting.svg?branch=0.5.0
:target: https://travis-ci.org/kezabelle/django-stagesetting.. |travis_master| image:: https://travis-ci.org/kezabelle/django-stagesetting.svg?branch=master
:target: https://travis-ci.org/kezabelle/django-stagesetting============== ======
Release Status
============== ======
stable (0.5.0) |travis_stable|
master |travis_master|
============== ======Pre-requisites
--------------The following versions are tested:
* Python 2.7, or 3.3+
* `Django`_ 1.7+Installation
------------First up, you need to install it (via `pip`_ as usual)::
pip install django-stagesetting==0.5.0
Once that's downloaded, add the package to your ``INSTALLED_APPS``
in your settings::INSTALLED_APPS = (
# ...
'stagesetting',
# ...
)do a migrate::
python manage.py migrate stagesetting
Add a ``STAGESETTINGS`` dictionary to your project's settings::
STAGESETTINGS = {
'SETTING_NAME': '...',
'ANOTHER_SETTING_NAME': '...',
}The setting collection name is the dictionary key, so must be unique.
Writing settings
^^^^^^^^^^^^^^^^
Settings may be created in a number of ways, the simplest of which is to
provide a ``dictionary`` as 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 a ``list`` or a ``tuple``, where
the *first item* represents a form (either a ``dictionary`` as above, **OR**
the ``dotted.path.to.a.Form.Class`` if you need custom validation) and the
*second, optional item* is 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 the ``dotted.path.Format``) might look like::
from django.core.exceptions import ValidationError
from django.forms import Form, DateFieldclass 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 normal `Form`_. Internally, this form's
``cleaned_data`` will be converted into `JSON`_ before being saved to the
database.
It will get re-converted to proper Python values when pulled out
of the database, by going through the given `Form`_ class's validation again,
including converting to rich values like model instances.Python types which can be detected
**********************************When detecting a dictionary as the value and auto-generating a form, the
following translations will be applied:- ``None`` becomes `NullBooleanField`_
- ``datetime.datetime`` becomes `DateTimeField`_
- ``datetime.date`` becomes `DateField`_
- ``datetime.time`` becomes `TimeField`_
- ``datetime.timedelta`` becomes `DurationField`_
- ``decimal.Decimal`` becomes `DecimalField`_
- ``float`` becomes `FloatField`_
- ``True`` or ``False`` become `BooleanField`_
- ``int`` becomes `IntegerField`_
- ``uuid.UUID`` becomes `UUIDField`_ or `RegexField`_, depending on the `Django`_ version
- ``list`` and ``tuple`` become `MultipleChoiceField`_
- ``collections.OrderedDict``, ``set``, ``frozenset``, and ``dict`` become `ChoiceField`_
- ``models.Model`` instances become `ModelChoiceField`_
- ``models.QuerySet`` becomes `ModelMultipleChoiceField`_
- strings become one of the following, depending on what checks they pass:- `GenericIPAddressField`_
- `URLField`_
- `EmailField`_
- `SlugField`_
- `CharField`_
- `IntegerField`_
- `DecimalField`_
- `UUIDField`_ (or `RegexField`_, depending on the `Django`_ version)
- `DateTimeField`_
- `DateField`_
- `TimeField`_
- Some strings are **really** special, and will instead turn into one of the following:- if the string == ``STATIC_URL`` or ``STATICFILES_STORAGE`` the field will be
a `ChoiceField`_ whose options are all the files found by the
project's ``STATICFILES_FINDERS``.
- if the string == ``MEDIA_URL``or ``DEFAULT_FILE_STORAGE`` the field will be
a `ChoiceField`_ whose options are all the files found by
``DEFAULT_FILE_STORAGE``.
- if the string *starts with* ``STATIC_URL`` it will be the same as using
the ``STATIC_URL`` generated field, but is a regular expression for filtering
to only certain files (i.e. ``/static/.*\.css$`` would find only css files)
- if the string *starts with* ``MEDIA_URL`` it will be the same as above,
but for files found in ``DEFAULT_FILE_STORAGE``.
- if a string looks like it contains HTML, it will try to use `django-bleach`_
for sanitisation, and one of `django-ckeditor`_, `django-tinymce`_,
`django-markdown`_, `django-pagedown`_, or `django-epiceditor`_ for an
appropriate widget.Usage in code
-------------The best way to access the settings in your views is to include
``stagesetting.middleware.ApplyRuntimeSettings`` in your ``MIDDLEWARE_CLASSES``
which will ensure there is a ``request.stagesettings`` variable 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 the `Form`_ values, either the default ones
or those changed in the database.Usage in templates
------------------If you've already got ``request`` in your template, obviously you can continue
to use ``request.stagesettings`` if the middleware is wired up.If you don't have request, or you're not using the middleware,
``stagesetting.context_processors.runtime_settings`` provides a ``STAGESETTING``
template 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 request
--------------------------If you don't have the middleware, or are in a part of the code which doesn't
have a ``request``, 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 single ``RuntimeSettingWrapper`` around 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.Alternatives
------------Other 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-constance`_ is similar
- uses ``pickle`` to store an arbitrary python value; ``stagesetting`` only
stores stuff it can put into JSON and relies on `Django`_ `Forms`_ to inflate
the JSON back into python values.
- Has both database and redis backends; ``stagesetting`` only supports
the database, though it will only do one query most of the time.
- `django-dynamic-preferences`_ by the look of it.
- `django-solo`_ as well.
- `django-djconfig`_ looks similar in principle, insofar as it uses forms?
- `django-aboutconfig`_ maybe?
- `django-modelsettings`_ looks pretty similar - *define Django application settings with Django ORM models and edit them in the admin area.*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... _Django: https://docs.djangoproject.com/en/stable/
.. _admin site: https://docs.djangoproject.com/en/stable/ref/contrib/admin/
.. _contrib.admin: https://docs.djangoproject.com/en/stable/ref/contrib/admin/
.. _Form: https://docs.djangoproject.com/en/stable/topics/forms/
.. _Forms: https://docs.djangoproject.com/en/stable/topics/forms/
.. _JSON: http://json.org/
.. _pip: https://pip.pypa.io/en/stable/
.. _pytest: http://pytest.org/latest/
.. _BooleanField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#booleanfield
.. _CharField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#charfield
.. _RegexField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#regexfield
.. _ChoiceField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#choicefield
.. _DateField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#datefield
.. _DateTimeField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#datetimefield
.. _DecimalField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#decimalfield
.. _DurationField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#durationfield
.. _EmailField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#emailfield
.. _FloatField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#floatfield
.. _GenericIPAddressField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#genericipaddressfield
.. _IntegerField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#integerfield
.. _ModelChoiceField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#modelchoicefield
.. _ModelMultipleChoiceField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#modelmultiplechoicefield
.. _MultipleChoiceField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#multiplechoicefield
.. _NullBooleanField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#nullbooleanfield
.. _SlugField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#slugfield
.. _TimeField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#timefield
.. _URLField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#urlfield
.. _UUIDField: https://docs.djangoproject.com/en/stable/ref/forms/fields/#uuidfield
.. _django-bleach: https://bitbucket.org/tim_heap/django-bleach
.. _django-ckeditor: https://github.com/django-ckeditor/django-ckeditor
.. _django-tinymce: https://github.com/aljosa/django-tinymce
.. _django-markdown: https://github.com/klen/django_markdown
.. _django-pagedown: https://github.com/timmyomahony/django-pagedown
.. _django-epiceditor: https://github.com/barraq/django-epiceditor
.. _django-constance: https://github.com/jezdez/django-constance
.. _django-dynamic-preferences: https://github.com/EliotBerriot/django-dynamic-preferences
.. _django-solo: https://github.com/lazybird/django-solo
.. _django-djconfig: https://github.com/nitely/django-djconfig
.. _django-aboutconfig: https://bitbucket.org/impala/django-aboutconfig
.. _django-modelsettings: https://github.com/IlyaSemenov/django-modelsettings