Author: leidel
Date: Sat Nov  1 17:05:38 2008
New Revision: 58

Added:
    trunk/dbtemplates/cache.py
Modified:
    trunk/dbtemplates/loader.py
    trunk/dbtemplates/models.py

Log:
Refactored template loader to use new backend architecture, that populates  
the cache by using signals.
Adds a BaseCacheBackend class with a simple API (load, save, remove) to be  
subclassed and specified in the DBTEMPLATES_CACHE_BACKEND settings.
Adds DjangoCacheBackend (uses Django's own caching mechanism) and  
FileSystemBackend (simple approach to filesystem saving).

Added: trunk/dbtemplates/cache.py
==============================================================================
--- (empty file)
+++ trunk/dbtemplates/cache.py  Sat Nov  1 17:05:38 2008
@@ -0,0 +1,97 @@
+import os
+from django.conf import settings
+from django.core.cache import cache
+from django.contrib.sites.models import Site
+from django.template import TemplateDoesNotExist
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.encoding import smart_unicode, force_unicode
+
+class BaseCacheBackend(object):
+    """
+    Base class for custom cache backend of dbtemplates to be used while
+    subclassing.
+
+    Set DBTEMPLATES_CACHE_BACKEND setting to the Python path to that  
subclass.
+    """
+    def __init__(self):
+        self.site = Site.objects.get_current()
+
+    def load(self, name):
+        """
+        Loads a template from the cache with the given name.
+        """
+        raise NotImplemented
+
+    def save(self, name, content):
+        """
+        Saves the given template content with the given name in the cache.
+        """
+        raise NotImplemented
+
+    def remove(self, name):
+        """
+        Removes the template with the given name from the cache.
+        """
+        raise NotImplemented
+
+class DjangoCacheBackend(BaseCacheBackend):
+    """
+    A cache backend that uses Django's cache mechanism.
+    """
+    def _cache_key(self, name):
+        return 'dbtemplates::%s' % name
+
+    def load(self, name):
+        cache_key = self._cache_key(name)
+        return cache.get(cache_key)
+
+    def save(self, name, content):
+        cache_key = self._cache_key(name)
+        cache.set(cache_key, content)
+
+    def remove(self, name):
+        cache_key = self._cache_key(name)
+        cache.delete(cache_key)
+
+class FileSystemBackend(BaseCacheBackend):
+    """
+    A cache backend that uses simple files to hold the template cache.
+    """
+    def __init__(self):
+        try:
+            self.cache_dir = getattr(settings, 'DBTEMPLATES_CACHE_DIR',  
None)
+            self.cache_dir = os.path.normpath(self.cache_dir)
+            if not os.path.isdir(self.cache_dir):
+                raise Exception
+        except:
+            raise ImproperlyConfigured('You\'re using the dbtemplates\'  
file system cache backend without having set the DBTEMPLATES_CACHE_DIR  
setting to a valid value. Make sure the directory exists and is writeable  
for the user your Django instance is running with.')
+        super(FileSystemBackend, self).__init__()
+
+    def _filepath(self, name):
+        return os.path.join(self.cache_dir, name)
+
+    def load(self, name):
+        try:
+            filepath = self._filepath(name)
+            return open(filepath).read().decode('utf-8')
+        except:
+            raise None
+
+    def save(self, name, content, retry=False):
+        try:
+            filepath = self._filepath(name)
+            dirname = os.path.dirname(filepath)
+            if not os.path.exists(dirname):
+                os.makedirs(dirname)
+            cache_file = open(filepath, 'w')
+            cache_file.write(force_unicode(content).encode('utf-8'))
+            cache_file.close()
+        except Exception:
+            raise
+
+    def remove(self, name):
+        try:
+            filepath = self._filepath(name)
+            os.remove(filepath)
+        except:
+            pass

Modified: trunk/dbtemplates/loader.py
==============================================================================
--- trunk/dbtemplates/loader.py (original)
+++ trunk/dbtemplates/loader.py Sat Nov  1 17:05:38 2008
@@ -1,75 +1,32 @@
  import os
  from django.conf import settings
-from django.db.models import signals
  from django.template import TemplateDoesNotExist
-from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured

-from dbtemplates.models import Template
+from dbtemplates.models import Template, backend

-try:
-    site = Site.objects.get_current()
-except:
-    site = None
-
-try:
-    cache_dir =  
os.path.normpath(getattr(settings, 'DBTEMPLATES_CACHE_DIR', None))
-    if not os.path.isdir(cache_dir):
-        raise
-except:
-    cache_dir = None
-
  def load_template_source(template_name, template_dirs=None):
      """
-    Tries to load the template from DBTEMPLATES_CACHE_DIR. If it does not  
exist
-    loads templates from the database by querying the database field  
``name``
-    with a template path and ``sites`` with the current site,
-    and tries to save the template as DBTEMPLATES_CACHE_DIR/``name`` for  
subsequent
-    requests.
-    If DBTEMPLATES_CACHE_DIR is not configured falls back to database-only  
operation.
-    """
-    if site is not None:
-        if cache_dir is not None:
-            filepath = os.path.join(cache_dir, template_name)
-            try:
-                return (open(filepath).read(), filepath)
-            except IOError:
-                try:
-                    t = Template.objects.get(name__exact=template_name,  
sites__pk=site.id)
-                    try:
-                        f = open(filepath, 'w')
-                        f.write(t.content)
-                        f.close()
-                    except IOError:
-                            try:
-                                head, tail = os.path.split(filepath)
-                                if head and not os.path.isdir(head):
-                                    os.makedirs(head)
-                            except Exception:
-                                pass
-
-                    return (t.content, 'db:%s:%s' %  
(settings.DATABASE_ENGINE, template_name))
-                except:
-                    pass
-        else:
-            try:
-                t = Template.objects.get(name__exact=template_name,  
sites__pk=site.id)
-                return (t.content, 'db:%s:%s' % (settings.DATABASE_ENGINE,  
template_name))
-            except:
-                pass
-    raise TemplateDoesNotExist, template_name
-load_template_source.is_usable = True
-
-def remove_cached_template(instance, **kwargs):
+    Tries to load the template from DBTEMPLATES_CACHE_DIR. If it does not
+    exist loads templates from the database by querying the database field
+    ``name`` with a template path and ``sites`` with the current site,
+    and tries to save the template as DBTEMPLATES_CACHE_DIR/``name`` for
+    subsequent requests. If DBTEMPLATES_CACHE_DIR is not configured falls
+    back to database-only operation.
      """
-    Called via django's signals to remove cached templates, if the  
template in the
-    database was changed or deleted.
-    """
-    if cache_dir is not None:
+    display_name = 'db:%s:%s' % (settings.DATABASE_ENGINE, template_name)
+    if backend:
          try:
-            filepath = os.path.join(cache_dir, instance.name)
-            os.remove(filepath)
-        except OSError:
+            backend_template = backend.load(template_name)
+            if backend_template is not None:
+                return backend_template, template_name
+        except:
              pass
-
-signals.post_save.connect(remove_cached_template, sender=Template)
-signals.pre_delete.connect(remove_cached_template, sender=Template)
+    try:
+        template = Template.objects.get(name__exact=template_name,
+                                        sites__pk=settings.SITE_ID)
+        return (template.content, display_name)
+    except:
+        pass
+    raise TemplateDoesNotExist, template_name
+load_template_source.is_usable = True

Modified: trunk/dbtemplates/models.py
==============================================================================
--- trunk/dbtemplates/models.py (original)
+++ trunk/dbtemplates/models.py Sat Nov  1 17:05:38 2008
@@ -1,11 +1,13 @@
  # -*- coding: utf-8 -*-
  from datetime import datetime
  from django.db import models
+from django.conf import settings
+from django.db.models import signals
  from django.contrib.sites.models import Site
  from django.utils.translation import gettext_lazy as _
  from django.template import TemplateDoesNotExist
  from django.template.loader import find_template_source
-
+from django.core.exceptions import ImproperlyConfigured

  class Template(models.Model):
      """
@@ -14,7 +16,7 @@
      """
      name = models.CharField(_('name'), unique=True, max_length=100,  
help_text=_("Example: 'flatpages/default.html'"))
      content = models.TextField(_('content'), blank=True)
-    sites = models.ManyToManyField(Site)
+    sites = models.ManyToManyField(Site, default=[settings.SITE_ID])
      creation_date = models.DateTimeField(_('creation date'),  
default=datetime.now)
      last_changed = models.DateTimeField(_('last changed'),  
default=datetime.now)

@@ -29,6 +31,8 @@

      def save(self, *args, **kwargs):
          self.last_changed = datetime.now()
+        # If content is empty look for a template with the given name and
+        # populate the template instance with its content.
          if not self.content:
              try:
                  source, origin = find_template_source(self.name)
@@ -38,6 +42,51 @@
                  pass
          super(Template, self).save(*args, **kwargs)

+# Check if django-reversion is installed and register the model with it if  
yes
+try:
+    models.get_app('reversion')
+except ImproperlyConfigured:
+    pass
+else:
+    import reversion
+    reversion.register(Template)
+
+def get_cache_backend():
+    path = getattr(settings, 'DBTEMPLATES_CACHE_BACKEND', False)
+    if path:
+        i = path.rfind('.')
+        module, attr = path[:i], path[i+1:]
+        try:
+            mod = __import__(module, {}, {}, [attr])
+        except ImportError, e:
+            raise ImproperlyConfigured, 'Error importing dbtemplates cache  
backend %s: "%s"' % (module, e)
+        try:
+            cls = getattr(mod, attr)
+        except AttributeError:
+            raise ImproperlyConfigured, 'Module "%s" does not define  
a "%s" cache backend' % (module, attr)
+        return cls()
+    return False
+
+backend = get_cache_backend()
+
+def add_template_to_cache(instance, **kwargs):
+    """
+    Called via Django's signals to cache the templates, if the template
+    in the database was added or changed.
+    """
+    backend.save(instance.name, instance.content)
+
+def remove_cached_template(instance, **kwargs):
+    """
+    Called via Django's signals to remove cached templates, if the template
+    in the database was changed or deleted.
+    """
+    backend.remove(instance.name)
+
+if backend:
+    signals.post_save.connect(remove_cached_template, sender=Template)
+    signals.post_save.connect(add_template_to_cache, sender=Template)
+    signals.pre_delete.connect(remove_cached_template, sender=Template)

  __test__ = {'API_TESTS':"""
  >>> from django.template import loader, Context

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pinax-updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pinax-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to