
from django.conf import settings
from django.db import DatabaseError

from mezzanine.conf.models import Setting


registry = {}


def register_setting(name="", editable=False, description="", default=None):
    """
    Registers a setting that can be edited via the admin.
    """
    default = getattr(settings, "MEZZANINE_%s" % name, default)
    registry[name] = {"name": name, "description": description, "editable":
        editable, "default": default, "type": type(default)}


def editable_settings(names=None):
    """
    Returns the names of editable settings.
    """
    if names is None:
        names = registry.keys()
    return [k for (k, v) in registry.items() if v["editable"] and k in names]


class Settings(object):
    """
    An object that provides settings via dynamic attribute access. Settings 
    that are registered as editable and can therefore be stored in the 
    database are *all* loaded once only, the first time *any* editable 
    setting is accessed. When accessing uneditable settings their default 
    values are used. The Settings object also provides access to Django 
    settings via django.conf.settings in order to provide a consistent 
    method of access for all settings.
    """

    def __init__(self):
        self.use_editable()
    
    def use_editable(self):
        """
        Set the loaded flag to False if editable settings are enabled so that 
        settings will be loaded from the DB on next access.
        """
        self._loaded = not registry["SETTINGS_EDITABLE"]["default"]

    def __getattr__(self, name):
        try:
            setting = registry[name]
        except KeyError:
            # Try django.conf.settings if requested name isn't registered.
            from django.conf import settings
            try:
                setting = getattr(settings, name)
            except AttributeError:
                raise AttributeError("Setting does not exist: %s" % name)
            return setting
        if self._loaded or not setting["editable"]:
            return setting["default"]
        # First time an editable setting is requested - load from DB.
        try:
            for setting in Setting.objects.all():
                setting_type = registry[setting.name]["type"]
                if setting_type is bool:
                    setting_value = setting.value != "False"
                else:
                    setting_value = setting_type(setting.value)
                setattr(self, setting.name, setting_value)
            self._loaded = True
            return getattr(self, name)
        except DatabaseError:
            # Allows for syncdb and other commands related to DB 
            # management to get up and running without the settings 
            # table existing.
            return setting["default"]


for app in settings.INSTALLED_APPS:
    try:
        __import__("%s.defaults" % app)
    except ImportError:
        pass
        
settings = Settings()

@@ -1543,16 +1543,50 @@
 efault%22%5D
+%0A        self._editable_cache = %7B%7D
 %0A%0A    de
@@ -1616,157 +1616,150 @@
 e):%0A
-   
+%0A
      
-try:%0A            setting = registry%5Bname%5D%0A        except KeyError:%0A            # Try django.conf.settings if requested name isn't registered.
+   # Lookup name as a registered setting or a Django setting.%0A        try:%0A            setting = registry%5Bname%5D%0A        except KeyError:
 %0A   
@@ -1816,38 +1816,14 @@
     
-try:%0A                setting =
+return
  get
@@ -1843,28 +1843,25 @@
 , name)%0A
-    
+%0A
         
 except A
@@ -1856,195 +1856,82 @@
     
-except AttributeError:%0A                raise AttributeError(%22Setting does not exist: %25s%22 %25 name)%0A            return setting%0A        if self._loaded or not setting%5B%22editable%22%5D:%0A   
+# First access for an editable setting - load from DB into cache.%0A
         
  ret
@@ -1918,39 +1918,34 @@
  cache.%0A        
- return
+if
  setting%5B%22defaul
@@ -1942,96 +1942,51 @@
 ng%5B%22
-default%22%5D%0A        # First time an editable setting is requested - load from DB.%0A
+editable%22%5D and not self._loaded:%0A  
         
 try:
@@ -1973,37 +1973,43 @@
 aded:%0A          
+  
 try:%0A
+    
             for 
@@ -2015,16 +2015,20 @@
  setting
+_obj
  in Sett
@@ -2054,32 +2054,36 @@
                 
+   
+ 
 setting_type = r
@@ -2093,24 +2093,28 @@
 stry%5Bsetting
+_obj
 .name%5D%5B%22type
@@ -2124,32 +2124,36 @@
                 
+  
+  
 if setting_type 
@@ -2161,16 +2161,20 @@
 s bool:%0A
+    
         
@@ -2204,24 +2204,28 @@
 ue = setting
+_obj
 .value != %22F
@@ -2250,14 +2250,22 @@
     
+    
 else:%0A
+    
     
@@ -2316,16 +2316,20 @@
 (setting
+_obj
 .value)%0A
@@ -2348,35 +2348,52 @@
     
-setattr(self, 
+    self._editable_cache%5B
 setting
+_obj
 .name
-,
+%5D =
  set
@@ -2398,26 +2398,29 @@
 etting_value
-)
 %0A
+    
             
@@ -2447,43 +2447,8 @@
     
-        return getattr(self, name)%0A
     
@@ -2469,24 +2469,28 @@
 abaseError:%0A
+    
             
@@ -2555,16 +2555,20 @@
         
+ 
+   
 # manage
@@ -2628,16 +2628,20 @@
         
+   
+ 
 # table 
@@ -2650,24 +2650,270 @@
 isting.%0A    
+            pass%0A%0A        # Use cached editable setting if found, otherwise use default.%0A        try:%0A            setting_value = self._editable_cache%5Bname%5D%0A        except KeyError:%0A            pass%0A        else:%0A            return setting_value%0A
         retu

Refactor settings loading to reload settings when ``use_editable`` called.