django-orchestra/orchestra/contrib/settings/__init__.py

114 lines
4.2 KiB
Python
Raw Permalink Normal View History

2015-05-04 19:52:53 +00:00
import re
from collections import OrderedDict
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.functional import Promise
from orchestra.core import validators
from orchestra.utils.python import import_class, format_exception
default_app_config = 'orchestra.contrib.settings.apps.SettingsConfig'
class Setting(object):
"""
Keeps track of the defined settings and provides extra batteries like value validation.
"""
conf_settings = settings
settings = OrderedDict()
def __str__(self):
return self.name
def __repr__(self):
value = str(self.value)
value = ("'%s'" if isinstance(value, str) else '%s') % value
return '<%s: %s>' % (self.name, value)
def __new__(cls, name, default, help_text="", choices=None, editable=True, serializable=True,
multiple=False, validators=[], types=[], call_init=False):
if call_init:
return super(Setting, cls).__new__(cls)
cls.settings[name] = cls(name, default, help_text=help_text, choices=choices, editable=editable,
serializable=serializable, multiple=multiple, validators=validators, types=types, call_init=True)
return cls.get_value(name, default)
def __init__(self, *args, **kwargs):
self.name, self.default = args
for name, value in kwargs.items():
setattr(self, name, value)
self.value = self.get_value(self.name, self.default)
self.settings[name] = self
@classmethod
def validate_choices(cls, value):
if not isinstance(value, (list, tuple)):
2015-10-05 13:31:08 +00:00
raise ValidationError("%s is not a valid choices." % value)
2015-05-04 19:52:53 +00:00
for choice in value:
if not isinstance(choice, (list, tuple)) or len(choice) != 2:
2015-10-05 13:31:08 +00:00
raise ValidationError("%s is not a valid choice." % choice)
2015-05-04 19:52:53 +00:00
value, verbose = choice
if not isinstance(verbose, (str, Promise)):
raise ValidationError("%s is not a valid verbose name." % value)
@classmethod
def validate_import_class(cls, value):
try:
import_class(value)
except Exception as exc:
raise ValidationError(format_exception(exc))
@classmethod
def validate_model_label(cls, value):
from django.apps import apps
try:
apps.get_model(*value.split('.'))
except Exception as exc:
raise ValidationError(format_exception(exc))
@classmethod
def string_format_validator(cls, names, modulo=True):
def validate_string_format(value, names=names, modulo=modulo):
errors = []
regex = r'%\(([^\)]+)\)' if modulo else r'{([^}]+)}'
for n in re.findall(regex, value):
if n not in names:
errors.append(
ValidationError('%s is not a valid format name.' % n)
)
if errors:
raise ValidationError(errors)
return validate_string_format
2015-10-04 19:57:00 +00:00
def validate_value(self, value):
if value:
validators.all_valid(value, self.validators)
2015-05-04 19:52:53 +00:00
valid_types = list(self.types)
2015-10-04 19:57:00 +00:00
if self.choices:
choices = self.choices
if callable(choices):
choices = choices()
choices = [n for n,v in choices]
values = value
if not isinstance(values, (list, tuple)):
values = [value]
for cvalue in values:
if cvalue not in choices:
raise ValidationError("'%s' not in '%s'" % (value, ', '.join(choices)))
2015-05-04 19:52:53 +00:00
if isinstance(self.default, (list, tuple)):
valid_types.extend([list, tuple])
valid_types.append(type(self.default))
2015-10-04 19:57:00 +00:00
if not isinstance(value, tuple(valid_types)):
2015-05-04 19:52:53 +00:00
raise ValidationError("%s is not a valid type (%s)." %
2015-10-04 19:57:00 +00:00
(type(value).__name__, ', '.join(t.__name__ for t in valid_types))
2015-05-04 19:52:53 +00:00
)
2015-10-04 19:57:00 +00:00
def validate(self):
self.validate_value(self.value)
2015-05-04 19:52:53 +00:00
@classmethod
def get_value(cls, name, default):
return getattr(cls.conf_settings, name, default)