Skip to content

Commit

Permalink
lazy load
Browse files Browse the repository at this point in the history
  • Loading branch information
nitely committed Jan 9, 2015
1 parent c294af1 commit 29f3284
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 89 deletions.
79 changes: 4 additions & 75 deletions djconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,9 @@

from __future__ import unicode_literals

from django.core.cache import get_cache
from django.db import connection
from django.conf import settings as django_settings

from djconfig.forms import ConfigForm
from djconfig.config import config, prefixer
from djconfig.models import Config as ConfigModel
from djconfig.settings import BACKEND, PREFIX
from djconfig.config import config
from djconfig.registry import register, load
from djconfig.settings import BACKEND

__version__ = "0.1.8"
__all__ = ['config', 'register']

_registered_forms = set()


def register(form_class):
"""
Register config forms
:param form_class: The form to be registered.
:type form_class: ConfigForm.
"""
global _registered_forms

assert issubclass(form_class, ConfigForm), \
"The form does not inherit from ConfigForm"

_registered_forms.add(form_class)
_check_backend()
_load()


def load():
"""
Loads every registered form into the cache.
If a field name is found in the db, it will load it from there.
Otherwise, the initial value from the field form is used.
"""
global _registered_forms

cache_values = {}
data = dict(ConfigModel.objects.all().values_list('key', 'value'))

for form_class in _registered_forms:
form = form_class(data=data)
form.is_valid()

initial = {prefixer(field_name): field.initial
for field_name, field in form.fields.items()}
cache_values.update(initial)

cleaned_data = {prefixer(field_name): value
for field_name, value in form.cleaned_data.items()
if field_name in data}
cache_values.update(cleaned_data)

cache_values[prefixer('_updated_at')] = data.get('_updated_at')
cache = get_cache(BACKEND)
cache.set_many(cache_values)


def _load():
"""
Avoids loading if the Config table does not exists.
ie: when running syncdb for the first time.
"""
if not ConfigModel._meta.db_table in connection.introspection.table_names():
return

load()


def _check_backend():
if django_settings.CACHES[BACKEND]['BACKEND'].endswith(".LocMemCache") and \
"djconfig.middleware.DjConfigLocMemMiddleware" not in django_settings.MIDDLEWARE_CLASSES:
raise ValueError("LocMemCache requires DjConfigLocMemMiddleware "
"but it was not found in MIDDLEWARE_CLASSES")
__all__ = ['config', 'register', 'load', 'BACKEND']
18 changes: 13 additions & 5 deletions djconfig/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@

from django.core.cache import get_cache

from djconfig.settings import BACKEND, PREFIX


def prefixer(key):
return "%s:%s" % (PREFIX, key)
from djconfig.settings import BACKEND
from djconfig.utils import prefixer
from djconfig import registry


class Config(object):

def __init__(self):
self._cache = get_cache(BACKEND)
self._is_loaded = False

def __getattr__(self, key):
self._lazy_load()
return self._cache.get(prefixer(key))

def _set(self, key, value):
self._lazy_load()
self._cache.set(prefixer(key), value)

def _lazy_load(self):
if self._is_loaded:
return

self._is_loaded = True
registry.load()


config = Config()
65 changes: 65 additions & 0 deletions djconfig/registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#-*- coding: utf-8 -*-

from __future__ import unicode_literals

from django.core.cache import get_cache
from django.conf import settings as django_settings

from djconfig.forms import ConfigForm
from djconfig.models import Config as ConfigModel
from djconfig.settings import BACKEND
from djconfig.utils import prefixer

_registered_forms = set()


def register(form_class):
"""
Register config forms
:param form_class: The form to be registered.
:type form_class: ConfigForm.
"""
global _registered_forms

assert issubclass(form_class, ConfigForm), \
"The form does not inherit from ConfigForm"

_registered_forms.add(form_class)
_check_backend()


def load():
"""
Loads every registered form into the cache.
If a field name is found in the db, it will load it from there.
Otherwise, the initial value from the field form is used.
"""
global _registered_forms

cache_values = {}
data = dict(ConfigModel.objects.all().values_list('key', 'value'))

for form_class in _registered_forms:
form = form_class(data=data)
form.is_valid()

initial = {prefixer(field_name): field.initial
for field_name, field in form.fields.items()}
cache_values.update(initial)

cleaned_data = {prefixer(field_name): value
for field_name, value in form.cleaned_data.items()
if field_name in data}
cache_values.update(cleaned_data)

cache_values[prefixer('_updated_at')] = data.get('_updated_at')
cache = get_cache(BACKEND)
cache.set_many(cache_values)


def _check_backend():
if django_settings.CACHES[BACKEND]['BACKEND'].endswith(".LocMemCache") and \
"djconfig.middleware.DjConfigLocMemMiddleware" not in django_settings.MIDDLEWARE_CLASSES:
raise ValueError("LocMemCache requires DjConfigLocMemMiddleware "
"but it was not found in MIDDLEWARE_CLASSES")
19 changes: 12 additions & 7 deletions djconfig/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from django.conf import settings

import djconfig
from djconfig import prefixer
from djconfig.registry import _registered_forms, load
from djconfig.utils import prefixer
from djconfig.forms import ConfigForm
from djconfig.models import Config as ConfigModel
from djconfig.config import Config as ConfigCache
Expand All @@ -35,7 +36,7 @@ class DjConfigTest(TestCase):

def setUp(self):
_cache.clear()
djconfig._registered_forms.clear()
_registered_forms.clear()

self.cache = get_cache(djconfig.BACKEND)

Expand All @@ -44,7 +45,7 @@ def test_register(self):
register forms
"""
djconfig.register(FooForm)
self.assertSetEqual(djconfig._registered_forms, {FooForm, })
self.assertSetEqual(_registered_forms, {FooForm, })

def test_register_invalid_form(self):
"""
Expand All @@ -60,6 +61,7 @@ def test_load(self):
Load initial configuration into the cache
"""
djconfig.register(FooForm)
load()
keys = ['boolean', 'boolean_false', 'char', 'email', 'float_number', 'integer', 'url']
values = self.cache.get_many([prefixer(k) for k in keys])
self.assertDictEqual(values, {prefixer('boolean'): True,
Expand All @@ -84,6 +86,7 @@ def test_load_from_database(self):
ConfigModel.objects.bulk_create(data)

djconfig.register(FooForm)
load()

keys = ['boolean', 'boolean_false', 'char', 'email', 'float_number', 'integer', 'url']
values = self.cache.get_many([prefixer(k) for k in keys])
Expand All @@ -106,6 +109,7 @@ def test_load_unicode(self):
"""
ConfigModel.objects.create(key='char', value=u"áéíóú")
djconfig.register(FooForm)
load()
self.assertEqual(self.cache.get(prefixer('char')), u"áéíóú")

def test_load_from_database_invalid(self):
Expand All @@ -114,6 +118,7 @@ def test_load_from_database_invalid(self):
"""
ConfigModel.objects.create(key='integer', value="string")
djconfig.register(FooForm)
load()
self.assertEqual(self.cache.get(prefixer('integer')), 123)

def test_load_updated_at(self):
Expand All @@ -139,7 +144,7 @@ class DjConfigFormsTest(TestCase):

def setUp(self):
_cache.clear()
djconfig._registered_forms.clear()
_registered_forms.clear()

def test_config_form(self):
"""
Expand Down Expand Up @@ -210,7 +215,7 @@ class DjConfigConfTest(TestCase):

def setUp(self):
_cache.clear()
djconfig._registered_forms.clear()
_registered_forms.clear()

def test_config(self):
"""
Expand All @@ -236,7 +241,7 @@ class DjConfigMiddlewareTest(TestCase):

def setUp(self):
_cache.clear()
djconfig._registered_forms.clear()
_registered_forms.clear()

def test_config_middleware_process_request(self):
"""
Expand Down Expand Up @@ -329,7 +334,7 @@ class DjConfigUtilsTest(TestCase):

def setUp(self):
_cache.clear()
djconfig._registered_forms.clear()
_registered_forms.clear()

def test_override_djconfig(self):
"""
Expand Down
8 changes: 7 additions & 1 deletion djconfig/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
from __future__ import unicode_literals
from functools import wraps

from . import config
from djconfig.settings import PREFIX


def prefixer(key):
return "%s:%s" % (PREFIX, key)


def override_djconfig(**new_cache_values):
"""
This is similar to Django's @override_settings, use it in testing.
"""
from . import config

def decorator(func):
@wraps(func)
def func_wrapper(*args, **kw):
Expand Down
2 changes: 1 addition & 1 deletion example/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
}

0 comments on commit 29f3284

Please sign in to comment.