mirror of
https://github.com/jazzband/django-dbtemplates.git
synced 2026-03-16 22:20:28 +00:00
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). git-svn-id: https://django-dbtemplates.googlecode.com/svn/trunk@58 cfb8ba98-e953-0410-9cff-959ffddf5974 committer: leidel <leidel@cfb8ba98-e953-0410-9cff-959ffddf5974> --HG-- extra : convert_revision : fde60d8ab2e834d898d9f662cf013d5546370eeb
This commit is contained in:
parent
391b00c655
commit
41ff350628
3 changed files with 170 additions and 67 deletions
97
dbtemplates/cache.py
Normal file
97
dbtemplates/cache.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
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
|
||||
display_name = 'db:%s:%s' % (settings.DATABASE_ENGINE, template_name)
|
||||
if backend:
|
||||
try:
|
||||
backend_template = backend.load(template_name)
|
||||
if backend_template is not None:
|
||||
return backend_template, template_name
|
||||
except:
|
||||
pass
|
||||
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
|
||||
|
||||
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.
|
||||
"""
|
||||
if cache_dir is not None:
|
||||
try:
|
||||
filepath = os.path.join(cache_dir, instance.name)
|
||||
os.remove(filepath)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
signals.post_save.connect(remove_cached_template, sender=Template)
|
||||
signals.pre_delete.connect(remove_cached_template, sender=Template)
|
||||
|
|
|
|||
|
|
@ -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 @@ class Template(models.Model):
|
|||
"""
|
||||
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 @@ class Template(models.Model):
|
|||
|
||||
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 @@ class Template(models.Model):
|
|||
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
|
||||
|
|
|
|||
Loading…
Reference in a new issue