From 6ffdd33cf51f56037e67cd532836d0262f0d2d90 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 6 Mar 2015 00:16:46 +0100 Subject: [PATCH] Restructure settings framework --- .../migrations/0014_auto_20150305_2310.py | 24 ++++ src/pretix/base/models.py | 103 +--------------- src/pretix/base/settings.py | 115 ++++++++++++++++++ src/pretix/base/tests/test_models.py | 3 +- 4 files changed, 146 insertions(+), 99 deletions(-) create mode 100644 src/pretix/base/migrations/0014_auto_20150305_2310.py create mode 100644 src/pretix/base/settings.py diff --git a/src/pretix/base/migrations/0014_auto_20150305_2310.py b/src/pretix/base/migrations/0014_auto_20150305_2310.py new file mode 100644 index 0000000000..cab2e01475 --- /dev/null +++ b/src/pretix/base/migrations/0014_auto_20150305_2310.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0013_merge'), + ] + + operations = [ + migrations.RenameField( + model_name='eventsetting', + old_name='event', + new_name='object', + ), + migrations.RenameField( + model_name='organizersetting', + old_name='organizer', + new_name='object', + ), + ] diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index d6ee02934f..a3c5099585 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -12,6 +12,7 @@ from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ from django.template.defaultfilters import date as _date from django.core.validators import RegexValidator +from pretix.base.settings import SettingsProxy import six from versions.models import Versionable as BaseVersionable from versions.models import VersionedForeignKey, VersionedManyToManyField, get_utc_now @@ -246,57 +247,12 @@ class Organizer(Versionable): def __str__(self): return self.name - class OrganizerSettingsProxy: - """ - This objects allows convenient access to settings stored in the - OrganizerSettings database model. It exposes all settings as properties - and it will do all the nasty defaults stuff for - you. It will return None for non-existing properties. - """ - - def __init__(self, organizer): - self._organizer = organizer - self._cached_obj = None - - def _cache(self): - if self._cached_obj is None: - self._cached_obj = {} - for setting in self._organizer.setting_objects.current.all(): - self._cached_obj[setting.key] = setting - return self._cached_obj - - def __getattr__(self, key): - if key in self._cache(): - return self._cache()[key].value - if key in OrganizerSetting.DEFAULTS: - return OrganizerSetting.DEFAULTS[key] - return None - - def __delattr__(self, key): - if key.startswith('_'): - return super().__delattr__(key) - if key in self._cache(): - self._cache()[key].delete() - del self._cache()[key] - - def __setattr__(self, key, value): - if key.startswith('_'): - return super().__setattr__(key, value) - if key in self._cache(): - s = self._cache()[key] - s = s.clone() - else: - s = OrganizerSetting(organizer=self._organizer, key=key) - s.value = value - s.save() - self._cache()[key] = s - @cached_property def settings(self): """ Returns an object representing this organizer's settings """ - return Organizer.OrganizerSettingsProxy(self) + return SettingsProxy(self, type=OrganizerSetting) class OrganizerPermission(Versionable): @@ -453,55 +409,12 @@ class Event(Versionable): from pretix.base.cache import EventRelatedCache return EventRelatedCache(self) - class EventSettingsProxy: - """ - This objects allows convenient access to settings stored in the - EventSettings database model. It exposes all settings as properties - and it will do all the nasty inheritance and defaults stuff for - you. It will return None for non-existing properties. - """ - - def __init__(self, event): - self._event = event - self._cached_obj = None - - def _cache(self): - if self._cached_obj is None: - self._cached_obj = {} - for setting in self._event.setting_objects.current.all(): - self._cached_obj[setting.key] = setting - return self._cached_obj - - def __getattr__(self, key): - if key in self._cache(): - return self._cache()[key].value - return getattr(self._event.organizer.settings, key) - - def __setattr__(self, key, value): - if key.startswith('_'): - return super().__setattr__(key, value) - if key in self._cache(): - s = self._cache()[key] - s = s.clone() - else: - s = EventSetting(event=self._event, key=key) - s.value = value - s.save() - self._cache()[key] = s - - def __delattr__(self, key): - if key.startswith('_'): - return super().__delattr__(key) - if key in self._cache(): - self._cache()[key].delete() - del self._cache()[key] - @cached_property def settings(self): """ Returns an object representing this event's settings """ - return Event.EventSettingsProxy(self) + return SettingsProxy(self, type=EventSetting, parent=self.organizer) class EventPermission(Versionable): @@ -1468,7 +1381,7 @@ class EventSetting(Versionable): An event settings is a key-value setting which can be set for a specific event """ - event = VersionedForeignKey(Event, related_name='setting_objects') + object = VersionedForeignKey(Event, related_name='setting_objects') key = models.CharField(max_length=255) value = models.TextField() @@ -1478,12 +1391,6 @@ class OrganizerSetting(Versionable): An event option is a key-value setting which can be set for an organizer. It will be inherited by the events of this organizer """ - DEFAULTS = { - 'user_mail_required': 'False', - 'max_items_per_order': '10', - 'attendee_names_asked': 'True', - 'attendee_names_required': 'False', - } - organizer = VersionedForeignKey(Organizer, related_name='setting_objects') + object = VersionedForeignKey(Organizer, related_name='setting_objects') key = models.CharField(max_length=255) value = models.TextField() diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py new file mode 100644 index 0000000000..7e4c99993a --- /dev/null +++ b/src/pretix/base/settings.py @@ -0,0 +1,115 @@ +DEFAULTS = { + 'user_mail_required': 'False', + 'max_items_per_order': '10', + 'attendee_names_asked': 'True', + 'attendee_names_required': 'False', +} + + +class SettingsProxy: + """ + This objects 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. It will return None for non-existing properties. + """ + + def __init__(self, obj, parent=None, type=None): + self._obj = obj + self._parent = parent + self._cached_obj = None + self._type = type + + def _cache(self): + if self._cached_obj is None: + self._cached_obj = {} + for setting in self._obj.setting_objects.current.all(): + self._cached_obj[setting.key] = setting + return self._cached_obj + + def get(self, key, default=None): + if key in self._cache(): + return self._cache()[key].value + value = None + if self._parent: + value = self._parent.settings.get(key) + if value is None and key in DEFAULTS: + return DEFAULTS[key] + if value is None and default is not None: + return default + return value + + def __getitem__(self, key): + return self.get(key) + + def __getattr__(self, key): + return self.get(key) + + def __setattr__(self, key, value): + if key.startswith('_'): + return super().__setattr__(key, value) + self.set(key, value) + + def __setitem__(self, key, value): + self.set(key, value) + + def set(self, key, value): + if key in self._cache(): + s = self._cache()[key] + s = s.clone() + else: + s = self._type(object=self._obj, key=key) + s.value = value + s.save() + self._cache()[key] = s + + def __delattr__(self, key): + if key.startswith('_'): + return super().__delattr__(key) + return self.__delitem__(key) + + def __delitem__(self, key): + if key in self._cache(): + self._cache()[key].delete() + del self._cache()[key] + + +class SettingsSandbox: + """ + Transparently proxied access to event settings, handling your domain- + prefixes for you. + """ + + def __init__(self, type, key, event): + self._event = event + self._type = type + self._key = key + + def _convert_key(self, key): + return '%s_%s_%s' % (self._type, self._key, key) + + def __setitem__(self, key, value): + self.set(key, value) + + def __setattr__(self, key, value): + if key.startswith('_'): + return super().__setattr__(key, value) + self.set(key, value) + + def __getattr__(self, item): + return self.get(item) + + def __getitem__(self, item): + return self.get(item) + + def __delitem__(self, key): + del self._event.settings[self._convert_key(key)] + + def __delattr__(self, key): + del self._event.settings[self._convert_key(key)] + + def get(self, key, default=None): + return self._event.settings.get(self._convert_key(key), default) + + def set(self, key, value): + self._event.settings.set(self._convert_key(key), value) diff --git a/src/pretix/base/tests/test_models.py b/src/pretix/base/tests/test_models.py index f8499d7b8d..ebb6d2b7b4 100644 --- a/src/pretix/base/tests/test_models.py +++ b/src/pretix/base/tests/test_models.py @@ -8,6 +8,7 @@ from pretix.base.models import ( Order, OrderPosition, CartPosition, OrganizerSetting) from pretix.base.types import VariationDict +from pretix.base import settings class ItemVariationsTest(TestCase): @@ -297,7 +298,7 @@ class QuotaTestCase(TestCase): class SettingsTestCase(TestCase): def setUp(self): - OrganizerSetting.DEFAULTS['test_default'] = 'def' + settings.DEFAULTS['test_default'] = 'def' self.organizer = Organizer.objects.create(name='Dummy', slug='dummy') self.event = Event.objects.create( organizer=self.organizer, name='Dummy', slug='dummy',