forked from CGM_Public/pretix_original
Integrate hierarkey package (#460)
This commit is contained in:
@@ -1,19 +1,14 @@
|
||||
import decimal
|
||||
import json
|
||||
from datetime import date, datetime, time
|
||||
from datetime import datetime
|
||||
|
||||
from django.core.cache import cache
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import dateutil.parser
|
||||
from django.conf import settings
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Model
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
from hierarkey.models import GlobalSettingsBase, Hierarkey
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pretix.base.models.settings import GlobalSetting
|
||||
from typing import Any
|
||||
|
||||
DEFAULTS = {
|
||||
'max_items_per_order': {
|
||||
@@ -382,194 +377,27 @@ Your {event} team"""))
|
||||
}
|
||||
}
|
||||
|
||||
settings_hierarkey = Hierarkey(attribute_name='settings')
|
||||
|
||||
class SettingsProxy:
|
||||
"""
|
||||
This object allows convenient access to settings stored in the
|
||||
EventSettings/OrganizerSettings database model. It exposes all settings as
|
||||
properties and it will do all the nasty inheritance and defaults stuff for
|
||||
you.
|
||||
"""
|
||||
for k, v in DEFAULTS.items():
|
||||
settings_hierarkey.add_default(k, v['default'], v['type'])
|
||||
|
||||
def __init__(self, obj: Model, parent: Optional[Model]=None, type=None):
|
||||
self._obj = obj
|
||||
self._parent = parent
|
||||
self._cached_obj = None
|
||||
self._write_cached_obj = None
|
||||
self._type = type
|
||||
|
||||
def _cache(self) -> Dict[str, Any]:
|
||||
if self._cached_obj is None:
|
||||
self._cached_obj = cache.get_or_set(
|
||||
'settings_{}_{}'.format(self._obj.settings_namespace, self._obj.pk),
|
||||
lambda: {s.key: s.value for s in self._obj.setting_objects.all()},
|
||||
timeout=1800
|
||||
)
|
||||
return self._cached_obj
|
||||
def i18n_uns(v):
|
||||
try:
|
||||
return LazyI18nString(json.loads(v))
|
||||
except ValueError:
|
||||
return LazyI18nString(str(v))
|
||||
|
||||
def _write_cache(self) -> Dict[str, Any]:
|
||||
if self._write_cached_obj is None:
|
||||
self._write_cached_obj = {
|
||||
s.key: s for s in self._obj.setting_objects.all()
|
||||
}
|
||||
return self._write_cached_obj
|
||||
|
||||
def _flush(self) -> None:
|
||||
self._cached_obj = None
|
||||
self._write_cached_obj = None
|
||||
self._flush_external_cache()
|
||||
settings_hierarkey.add_type(LazyI18nString,
|
||||
serialize=lambda s: json.dumps(s.data),
|
||||
unserialize=i18n_uns)
|
||||
|
||||
def _flush_external_cache(self):
|
||||
cache.delete('settings_{}_{}'.format(self._obj.settings_namespace, self._obj.pk))
|
||||
|
||||
def freeze(self) -> dict:
|
||||
"""
|
||||
Returns a dictionary of all settings set for this object, including
|
||||
any default values of its parents or hardcoded in pretix.
|
||||
"""
|
||||
settings = {}
|
||||
for key, v in DEFAULTS.items():
|
||||
settings[key] = self._unserialize(v['default'], v['type'])
|
||||
if self._parent:
|
||||
settings.update(self._parent.settings.freeze())
|
||||
for key in self._cache():
|
||||
settings[key] = self.get(key)
|
||||
return settings
|
||||
|
||||
def _unserialize(self, value: str, as_type: type, binary_file=False) -> Any:
|
||||
if as_type is None and value is not None and value.startswith('file://'):
|
||||
as_type = File
|
||||
|
||||
if as_type is not None and isinstance(value, as_type):
|
||||
return value
|
||||
elif value is None:
|
||||
return None
|
||||
elif as_type == int or as_type == float or as_type == decimal.Decimal:
|
||||
return as_type(value)
|
||||
elif as_type == dict or as_type == list:
|
||||
return json.loads(value)
|
||||
elif as_type == bool or value in ('True', 'False'):
|
||||
return value == 'True'
|
||||
elif as_type == File:
|
||||
try:
|
||||
fi = default_storage.open(value[7:], 'rb' if binary_file else 'r')
|
||||
fi.url = default_storage.url(value[7:])
|
||||
return fi
|
||||
except OSError:
|
||||
return False
|
||||
elif as_type == datetime:
|
||||
return dateutil.parser.parse(value)
|
||||
elif as_type == date:
|
||||
return dateutil.parser.parse(value).date()
|
||||
elif as_type == time:
|
||||
return dateutil.parser.parse(value).time()
|
||||
elif as_type == LazyI18nString and not isinstance(value, LazyI18nString):
|
||||
try:
|
||||
return LazyI18nString(json.loads(value))
|
||||
except ValueError:
|
||||
return LazyI18nString(str(value))
|
||||
elif as_type is not None and issubclass(as_type, Model):
|
||||
return as_type.objects.get(pk=value)
|
||||
return value
|
||||
|
||||
def _serialize(self, value: Any) -> str:
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
elif isinstance(value, int) or isinstance(value, float) \
|
||||
or isinstance(value, bool) or isinstance(value, decimal.Decimal):
|
||||
return str(value)
|
||||
elif isinstance(value, list) or isinstance(value, dict):
|
||||
return json.dumps(value)
|
||||
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
|
||||
return value.isoformat()
|
||||
elif isinstance(value, Model):
|
||||
return value.pk
|
||||
elif isinstance(value, LazyI18nString):
|
||||
return json.dumps(value.data)
|
||||
elif isinstance(value, File):
|
||||
return 'file://' + value.name
|
||||
|
||||
raise TypeError('Unable to serialize %s into a setting.' % str(type(value)))
|
||||
|
||||
def get(self, key: str, default=None, as_type: type=None, binary_file=False):
|
||||
"""
|
||||
Get a setting specified by key ``key``. Normally, settings are strings, but
|
||||
if you put non-strings into the settings object, you can request unserialization
|
||||
by specifying ``as_type``. If the key does not have a harcdoded type in the pretix source,
|
||||
omitting ``as_type`` always will get you a string.
|
||||
|
||||
If the setting with the specified name does not exist on this object, any parent object
|
||||
will be queried (e.g. the organizer of an event). If still no value is found, a default
|
||||
value hardcoded will be returned if one exists. If not, the value of the ``default`` argument
|
||||
will be returned instead.
|
||||
"""
|
||||
if as_type is None and key in DEFAULTS:
|
||||
as_type = DEFAULTS[key]['type']
|
||||
|
||||
if key in self._cache():
|
||||
value = self._cache()[key]
|
||||
else:
|
||||
value = None
|
||||
if self._parent:
|
||||
value = self._parent.settings.get(key, as_type=str)
|
||||
if value is None and key in DEFAULTS:
|
||||
value = DEFAULTS[key]['default']
|
||||
if value is None and default is not None:
|
||||
value = default
|
||||
|
||||
return self._unserialize(value, as_type, binary_file=binary_file)
|
||||
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
return self.get(key)
|
||||
|
||||
def __getattr__(self, key: str) -> Any:
|
||||
if key.startswith('_'):
|
||||
return super().__getattr__(key)
|
||||
return self.get(key)
|
||||
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
self.set(key, value)
|
||||
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
self.set(key, value)
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
Stores a setting to the database of its object.
|
||||
"""
|
||||
wc = self._write_cache()
|
||||
if key in wc:
|
||||
s = wc[key]
|
||||
else:
|
||||
s = self._type(object=self._obj, key=key)
|
||||
s.value = self._serialize(value)
|
||||
s.save()
|
||||
self._cache()[key] = s.value
|
||||
wc[key] = s
|
||||
self._flush_external_cache()
|
||||
|
||||
def __delattr__(self, key: str) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__delattr__(key)
|
||||
self.delete(key)
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
self.delete(key)
|
||||
|
||||
def delete(self, key: str) -> None:
|
||||
"""
|
||||
Deletes a setting from this object's storage.
|
||||
"""
|
||||
if key in self._write_cache():
|
||||
self._write_cache()[key].delete()
|
||||
del self._write_cache()[key]
|
||||
|
||||
if key in self._cache():
|
||||
del self._cache()[key]
|
||||
|
||||
self._flush_external_cache()
|
||||
@settings_hierarkey.set_global(cache_namespace='global')
|
||||
class GlobalSettingsObject(GlobalSettingsBase):
|
||||
slug = '_global'
|
||||
|
||||
|
||||
class SettingsSandbox:
|
||||
@@ -614,13 +442,3 @@ class SettingsSandbox:
|
||||
|
||||
def set(self, key: str, value: Any):
|
||||
self._event.settings.set(self._convert_key(key), value)
|
||||
|
||||
|
||||
class GlobalSettingsObject():
|
||||
settings_namespace = 'global'
|
||||
|
||||
def __init__(self):
|
||||
self.settings = SettingsProxy(self, type=GlobalSetting)
|
||||
self.setting_objects = GlobalSetting.objects
|
||||
self.slug = '_global'
|
||||
self.pk = '_global'
|
||||
|
||||
Reference in New Issue
Block a user