114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
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)):
|
|
raise ValidationError("%s is not a valid choices." % str(value))
|
|
for choice in value:
|
|
if not isinstance(choice, (list, tuple)) or len(choice) != 2:
|
|
raise ValidationError("%s is not a valid choice." % str(choice))
|
|
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
|
|
|
|
def validate_value(self, value):
|
|
if value:
|
|
validators.all_valid(value, self.validators)
|
|
valid_types = list(self.types)
|
|
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)))
|
|
if isinstance(self.default, (list, tuple)):
|
|
valid_types.extend([list, tuple])
|
|
valid_types.append(type(self.default))
|
|
if not isinstance(value, tuple(valid_types)):
|
|
raise ValidationError("%s is not a valid type (%s)." %
|
|
(type(value).__name__, ', '.join(t.__name__ for t in valid_types))
|
|
)
|
|
|
|
def validate(self):
|
|
self.validate_value(self.value)
|
|
|
|
@classmethod
|
|
def get_value(cls, name, default):
|
|
return getattr(cls.conf_settings, name, default)
|