mirror of
https://github.com/jazzband/django-constance.git
synced 2026-03-16 22:40:24 +00:00
Merge pull request #108 from PetrDlouhy/custom_fields
Allow to override field config_type and set custom additional fields
This commit is contained in:
commit
803cf465c7
7 changed files with 77 additions and 7 deletions
|
|
@ -15,7 +15,9 @@ from django.template.response import TemplateResponse
|
|||
from django.utils import six
|
||||
from django.utils.encoding import smart_bytes
|
||||
from django.utils.formats import localize
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import django
|
||||
|
||||
|
||||
from . import LazyConfig, settings
|
||||
|
|
@ -42,6 +44,25 @@ FIELDS = {
|
|||
float: (fields.FloatField, {'widget': NUMERIC_WIDGET}),
|
||||
}
|
||||
|
||||
|
||||
def parse_additional_fields(fields):
|
||||
for key in fields:
|
||||
field = fields[key]
|
||||
|
||||
field[0] = import_string(field[0])
|
||||
|
||||
if 'widget' in field[1]:
|
||||
klass = import_string(field[1]['widget'])
|
||||
field[1]['widget'] = klass(**(field[1].get('widget_kwargs', {}) or {}))
|
||||
|
||||
if 'widget_kwargs' in field[1]:
|
||||
del field[1]['widget_kwargs']
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
FIELDS.update(parse_additional_fields(settings.ADDITIONAL_FIELDS))
|
||||
|
||||
if not six.PY3:
|
||||
FIELDS.update({
|
||||
long: INTEGER_LIKE,
|
||||
|
|
@ -56,8 +77,13 @@ class ConstanceForm(forms.Form):
|
|||
super(ConstanceForm, self).__init__(*args, initial=initial, **kwargs)
|
||||
version_hash = hashlib.md5()
|
||||
|
||||
for name, (default, help_text) in settings.CONFIG.items():
|
||||
config_type = type(default)
|
||||
for name, options in settings.CONFIG.items():
|
||||
default, help_text = options[0], options[1]
|
||||
if len(options) == 3:
|
||||
config_type = options[2]
|
||||
else:
|
||||
config_type = type(default)
|
||||
|
||||
if config_type not in FIELDS:
|
||||
raise ImproperlyConfigured(_("Constance doesn't support "
|
||||
"config values of the type "
|
||||
|
|
@ -103,8 +129,8 @@ class ConstanceAdmin(admin.ModelAdmin):
|
|||
# First load a mapping between config name and default value
|
||||
if not self.has_change_permission(request, None):
|
||||
raise PermissionDenied
|
||||
default_initial = ((name, default)
|
||||
for name, (default, help_text) in settings.CONFIG.items())
|
||||
default_initial = ((name, options[0])
|
||||
for name, options in settings.CONFIG.items())
|
||||
# Then update the mapping with actually values from the backend
|
||||
initial = dict(default_initial,
|
||||
**dict(config._backend.mget(settings.CONFIG.keys())))
|
||||
|
|
@ -128,7 +154,8 @@ class ConstanceAdmin(admin.ModelAdmin):
|
|||
'form': form,
|
||||
'media': self.media + form.media,
|
||||
}
|
||||
for name, (default, help_text) in settings.CONFIG.items():
|
||||
for name, options in settings.CONFIG.items():
|
||||
default, help_text = options[0], options[1]
|
||||
# First try to load the value from the actual backend
|
||||
value = initial.get(name)
|
||||
# Then if the returned value is None, get the default
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ class Config(object):
|
|||
|
||||
def __getattr__(self, key):
|
||||
try:
|
||||
default, help_text = settings.CONFIG[key]
|
||||
if not len(settings.CONFIG[key]) in (2, 3):
|
||||
raise AttributeError(key)
|
||||
default = settings.CONFIG[key][0]
|
||||
except KeyError:
|
||||
raise AttributeError(key)
|
||||
result = self._backend.get(key)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ BACKEND = getattr(settings, 'CONSTANCE_BACKEND',
|
|||
|
||||
CONFIG = getattr(settings, 'CONSTANCE_CONFIG', {})
|
||||
|
||||
ADDITIONAL_FIELDS = getattr(settings, 'CONSTANCE_ADDITIONAL_FIELDS', {})
|
||||
|
||||
DATABASE_CACHE_BACKEND = getattr(settings, 'CONSTANCE_DATABASE_CACHE_BACKEND',
|
||||
None)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
{% block bodyclass %}change-list{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<div id="content-main" class="constance">
|
||||
<div class="module" id="changelist">
|
||||
<form id="changelist-form" action="" method="post">{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,33 @@ admin will show.
|
|||
See the :ref:`Backends <backends>` section how to setup the backend and
|
||||
finish the configuration.
|
||||
|
||||
Custom fields
|
||||
-------------
|
||||
|
||||
You can set the field type by the third value in the `CONSTANCE_CONFIG`
|
||||
tuple. The value can be string or one of the supported types:
|
||||
|
||||
.. code-block:: python
|
||||
'THE_ANSWER': (42, 'Answer to the Ultimate Question of Life, '
|
||||
'The Universe, and Everything', str),
|
||||
|
||||
If you can add your custom field types, you can use the
|
||||
`CONSTANCE_ADDITIONAL_FIELDS` variable. Note that you must
|
||||
use later evaluated strings instead of direct classes:
|
||||
|
||||
.. code-block:: python
|
||||
CONSTANCE_ADDITIONAL_FIELDS = {
|
||||
'yes_no_null_select': ['django.forms.fields.ChoiceField',
|
||||
{
|
||||
'widget': 'django.forms.Select',
|
||||
'choices': (("-----", None), ("yes", "Yes"), ("no", "No"))
|
||||
}],
|
||||
}
|
||||
|
||||
CONSTANCE_CONFIG = {
|
||||
'MY_SELECT_KEY': ('yes', 'select yes or no', 'yes_no_null_select'),
|
||||
}
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,14 @@ long_value = 123456
|
|||
if not six.PY3:
|
||||
long_value = long(long_value)
|
||||
|
||||
CONSTANCE_ADDITIONAL_FIELDS = {
|
||||
'yes_no_null_select': ['django.forms.fields.ChoiceField',
|
||||
{
|
||||
'widget': 'django.forms.Select',
|
||||
'choices': (("-----", None), ("yes", "Yes"), ("no", "No"))
|
||||
}],
|
||||
}
|
||||
|
||||
CONSTANCE_CONFIG = {
|
||||
'INT_VALUE': (1, 'some int'),
|
||||
'LONG_VALUE': (long_value, 'some looong int'),
|
||||
|
|
@ -63,6 +71,7 @@ CONSTANCE_CONFIG = {
|
|||
'FLOAT_VALUE': (3.1415926536, 'PI'),
|
||||
'DATE_VALUE': (date(2010, 12, 24), 'Merry Chrismas'),
|
||||
'TIME_VALUE': (time(23, 59, 59), 'And happy New Year'),
|
||||
'CHOICE_VALUE': ('yes', 'select yes or no', 'yes_no_null_select'),
|
||||
'LINEBREAK_VALUE': ('Spam spam', 'eggs\neggs'),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class StorageTestsMixin(object):
|
|||
self.assertEqual(self.config.FLOAT_VALUE, 3.1415926536)
|
||||
self.assertEqual(self.config.DATE_VALUE, date(2010, 12, 24))
|
||||
self.assertEqual(self.config.TIME_VALUE, time(23, 59, 59))
|
||||
self.assertEqual(self.config.CHOICE_VALUE, 'yes')
|
||||
|
||||
# set values
|
||||
self.config.INT_VALUE = 100
|
||||
|
|
@ -39,6 +40,7 @@ class StorageTestsMixin(object):
|
|||
self.config.FLOAT_VALUE = 2.718281845905
|
||||
self.config.DATE_VALUE = date(2001, 12, 20)
|
||||
self.config.TIME_VALUE = time(1, 59, 0)
|
||||
self.config.CHOICE_VALUE = 'no'
|
||||
|
||||
# read again
|
||||
self.assertEqual(self.config.INT_VALUE, 100)
|
||||
|
|
@ -51,6 +53,7 @@ class StorageTestsMixin(object):
|
|||
self.assertEqual(self.config.FLOAT_VALUE, 2.718281845905)
|
||||
self.assertEqual(self.config.DATE_VALUE, date(2001, 12, 20))
|
||||
self.assertEqual(self.config.TIME_VALUE, time(1, 59, 0))
|
||||
self.assertEqual(self.config.CHOICE_VALUE, 'no')
|
||||
|
||||
def test_nonexistent(self):
|
||||
try:
|
||||
|
|
|
|||
Loading…
Reference in a new issue