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
-~----------~----~----~----~------~----~------~--~---