diff --git a/constance/backends.py b/constance/backends.py index 4ff915c..e69de29 100644 --- a/constance/backends.py +++ b/constance/backends.py @@ -1,84 +0,0 @@ -from django.core.exceptions import ImproperlyConfigured -from django.db.models.signals import post_save -from django.utils.functional import memoize - -from constance import settings -from constance.utils import import_module_attr - -try: - from cPickle import loads, dumps -except ImportError: - from pickle import loads, dumps - - -class Backend(object): - - def __init__(self, prefix): - self._prefix = prefix - - def get(self, key): - """ - Get the key from the backend store and return it. - Return None if not found. - """ - raise NotImplementedError - - def set(self, key, value): - """ - Add the value to the backend store given the key. - """ - raise NotImplementedError - -db_cache = {} - -class DatabaseBackend(Backend): - - def __init__(self, prefix): - super(DatabaseBackend, self).__init__(prefix) - from constance.models import Constance - if not Constance._meta.installed: - raise ImproperlyConfigured( - "The constance app isn't installed correctly. " - "Make sure it's listed in the INSTALLED_APPS setting.") - self._model = Constance - # Clear simple cache. - post_save.connect(self.clear, sender=self._model) - - def _get(self, key): - try: - value = self._model._default_manager.get(key=key).value - except self._model.DoesNotExist: - return None - return value - get = memoize(_get, db_cache, 2) - - def set(self, key, value): - constance, created = self._model._default_manager.get_or_create(key=key, defaults={'value': value}) - if not created: - constance.value = value - constance.save() - - def clear(self, sender, instance, created, **kwargs): - if not created: - db_cache.clear() - -class RedisBackend(Backend): - - def __init__(self, prefix): - super(RedisBackend, self).__init__(prefix) - connection_cls = settings.CONNECTION_CLASS - if connection_cls is not None: - self._rd = import_module_attr(connection_cls)() - else: - import redis - self._rd = redis.Redis(**settings.REDIS_CONNECTION) - - def get(self, key): - value = self._rd.get("%s%s" % (self._prefix, key)) - if value: - return loads(value) - return None - - def set(self, key, value): - self._rd.set("%s%s" % (self._prefix, key), dumps(value)) - diff --git a/constance/backends/__init__.py b/constance/backends/__init__.py new file mode 100644 index 0000000..1eb0a02 --- /dev/null +++ b/constance/backends/__init__.py @@ -0,0 +1,16 @@ + +class Backend(object): + + def get(self, key): + """ + Get the key from the backend store and return it. + Return None if not found. + """ + raise NotImplementedError + + def set(self, key, value): + """ + Add the value to the backend store given the key. + """ + raise NotImplementedError + diff --git a/constance/backends/database/__init__.py b/constance/backends/database/__init__.py new file mode 100644 index 0000000..2563923 --- /dev/null +++ b/constance/backends/database/__init__.py @@ -0,0 +1,37 @@ +from django.core.exceptions import ImproperlyConfigured +from django.db.models.signals import post_save +from django.utils.functional import memoize + +from constance.backends import Backend + +db_cache = {} + +class DatabaseBackend(Backend): + + def __init__(self): + from constance.backends.database.models import Constance + if not Constance._meta.installed: + raise ImproperlyConfigured( + "The constance.contrib.database app isn't installed correctly. " + "Make sure it's listed in the INSTALLED_APPS setting.") + self._model = Constance + # Clear simple cache. + post_save.connect(self.clear, sender=self._model) + + def _get(self, key): + try: + value = self._model._default_manager.get(key=key).value + except self._model.DoesNotExist: + return None + return value + get = memoize(_get, db_cache, 2) + + def set(self, key, value): + constance, created = self._model._default_manager.get_or_create(key=key, defaults={'value': value}) + if not created: + constance.value = value + constance.save() + + def clear(self, sender, instance, created, **kwargs): + if not created: + db_cache.clear() diff --git a/constance/backends/database/models.py b/constance/backends/database/models.py new file mode 100644 index 0000000..f5bc3c6 --- /dev/null +++ b/constance/backends/database/models.py @@ -0,0 +1,20 @@ +from django.db import models +from django.core.exceptions import ImproperlyConfigured + +from django.utils.translation import ugettext_lazy as _ + +try: + from picklefield import PickledObjectField +except ImportError: + raise ImproperlyConfigured("Couldn't find the the 3rd party app " + "django-picklefield which is required for " + "the constance database backend.") + +class Constance(models.Model): + key = models.TextField() + value = PickledObjectField() + + class Meta: + verbose_name = _('constance') + verbose_name_plural = _('constances') + db_table = 'constance_config' diff --git a/constance/backends/redis.py b/constance/backends/redis.py new file mode 100644 index 0000000..8b1a586 --- /dev/null +++ b/constance/backends/redis.py @@ -0,0 +1,29 @@ +from constance import settings, utils +from constance.backends import Backend + +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps + + +class RedisBackend(Backend): + + def __init__(self): + super(RedisBackend, self).__init__() + self._prefix = settings.PREFIX + connection_cls = settings.CONNECTION_CLASS + if connection_cls is not None: + self._rd = utils.import_module_attr(connection_cls)() + else: + import redis + self._rd = redis.Redis(**settings.REDIS_CONNECTION) + + def get(self, key): + value = self._rd.get("%s%s" % (self._prefix, key)) + if value: + return loads(value) + return None + + def set(self, key, value): + self._rd.set("%s%s" % (self._prefix, key), dumps(value)) diff --git a/constance/config.py b/constance/config.py index 24860ab..79751db 100644 --- a/constance/config.py +++ b/constance/config.py @@ -1,18 +1,17 @@ -from constance import settings -from constance.utils import import_module_attr +from constance import settings, utils class Config(object): """ The global config wrapper that handles the backend. """ def __init__(self): - super(Config, self).__setattr__( - '_backend', import_module_attr(settings.BACKEND)(settings.PREFIX)) + super(Config, self).__setattr__('_backend', + utils.import_module_attr(settings.BACKEND)()) def __getattr__(self, key): try: default, help_text = settings.CONFIG[key] - except KeyError, e: + except KeyError: raise AttributeError(key) result = self._backend.get(key) if result is None: diff --git a/constance/models.py b/constance/models.py deleted file mode 100644 index e4de4ed..0000000 --- a/constance/models.py +++ /dev/null @@ -1,25 +0,0 @@ -from constance import settings - -if settings.BACKEND == 'constance.backends.DatabaseBackend': - - from django.db import models - from django.core.exceptions import ImproperlyConfigured - - from django.utils.translation import ugettext_lazy as _ - - try: - from picklefield import PickledObjectField - except ImportError: - raise ImproperlyConfigured("Couldn't find the the 3rd party app " - "django-picklefield which is required for " - "the constance database backend.") - - class Constance(models.Model): - key = models.TextField() - value = PickledObjectField() - - class Meta: - verbose_name = _('constance') - verbose_name_plural = _('constances') - db_table = 'constance_config' - diff --git a/constance/settings.py b/constance/settings.py index 6756abc..a5948e1 100644 --- a/constance/settings.py +++ b/constance/settings.py @@ -5,13 +5,15 @@ settings = import_module_attr( os.getenv('CONSTANCE_SETTINGS_MODULE', 'django.conf.settings') ) -PREFIX = getattr(settings, 'CONSTANCE_PREFIX', 'constance:') +PREFIX = getattr(settings, 'CONSTANCE_REDIS_PREFIX', + getattr(settings, 'CONSTANCE_PREFIX', 'constance:')) -BACKEND = getattr(settings, 'CONSTANCE_BACKEND', 'constance.backends.DatabaseBackend') +BACKEND = getattr(settings, 'CONSTANCE_BACKEND', 'constance.backends.redis.RedisBackend') CONFIG = getattr(settings, 'CONSTANCE_CONFIG', {}) -CONNECTION_CLASS = getattr(settings, 'CONSTANCE_CONNECTION_CLASS', None) +CONNECTION_CLASS = getattr(settings, 'CONSTANCE_REDIS_CONNECTION_CLASS', + getattr(settings, 'CONSTANCE_CONNECTION_CLASS', None)) REDIS_CONNECTION = getattr(settings, 'CONSTANCE_REDIS_CONNECTION', getattr(settings, 'CONSTANCE_CONNECTION', {})) diff --git a/tests/testproject/settings.py b/tests/testproject/settings.py index 89163b5..5d8efc6 100644 --- a/tests/testproject/settings.py +++ b/tests/testproject/settings.py @@ -16,6 +16,7 @@ INSTALLED_APPS = ( 'django.contrib.admin', 'constance', + 'constance.backends.database', 'testproject.test_app', ) diff --git a/tests/testproject/test_app/tests/test_config.py b/tests/testproject/test_app/tests/test_config.py index 0b2cc47..cb3675d 100644 --- a/tests/testproject/test_app/tests/test_config.py +++ b/tests/testproject/test_app/tests/test_config.py @@ -75,7 +75,7 @@ class TestRedis(TestCase, TestStorage): def setUp(self): self.old_backend = settings.BACKEND - settings.BACKEND = 'constance.backends.RedisBackend' + settings.BACKEND = 'constance.backends.redis.RedisBackend' def tearDown(self): del sys.modules['constance'] @@ -86,7 +86,16 @@ class TestRedis(TestCase, TestStorage): constance.config = Config() class TestDatabase(TestCase, TestStorage): - pass + + def setUp(self): + self.old_backend = settings.BACKEND + settings.BACKEND = 'constance.backends.database.DatabaseBackend' + + def tearDown(self): + del sys.modules['constance'] + settings.BACKEND = self.old_backend + import constance + constance.config = Config() class TestAdmin(TestCase): model = Config