Moved setttings over to use AppSettings class.

This commit is contained in:
Jannis Leidel 2011-07-01 15:44:09 +02:00
parent ca032e70cf
commit a40204def7
9 changed files with 206 additions and 89 deletions

View file

@ -4,13 +4,13 @@ from django.contrib import admin
from django.utils.translation import ungettext, ugettext_lazy as _
from django.utils.safestring import mark_safe
from dbtemplates import settings
from dbtemplates.conf import settings
from dbtemplates.models import (Template,
remove_cached_template, add_template_to_cache)
# Check if django-reversion is installed and use reversions' VersionAdmin
# as the base admin class if yes
if settings.USE_REVERSION:
if settings.DBTEMPLATES_USE_REVERSION:
from reversion.admin import VersionAdmin as TemplateModelAdmin
else:
from django.contrib.admin import ModelAdmin as TemplateModelAdmin
@ -23,8 +23,8 @@ class CodeMirrorTextArea(forms.Textarea):
"""
class Media:
css = dict(screen=[
posixpath.join(settings.MEDIA_PREFIX, 'css/editor.css')])
js = [posixpath.join(settings.MEDIA_PREFIX, 'js/codemirror.js')]
posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX, 'css/editor.css')])
js = [posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX, 'js/codemirror.js')]
def render(self, name, value, attrs=None):
result = []
@ -43,15 +43,15 @@ class CodeMirrorTextArea(forms.Textarea):
lineNumbers: true
});
</script>
""" % dict(media_prefix=settings.MEDIA_PREFIX, name=name))
""" % dict(media_prefix=settings.DBTEMPLATES_MEDIA_PREFIX, name=name))
return mark_safe(u"".join(result))
if settings.USE_CODEMIRROR:
if settings.DBTEMPLATES_USE_CODEMIRROR:
TemplateContentTextArea = CodeMirrorTextArea
else:
TemplateContentTextArea = forms.Textarea
if settings.AUTO_POPULATE_CONTENT:
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT:
content_help_text = _("Leaving this empty causes Django to look for a "
"template with the given name and populate this field with its "
"content.")

42
dbtemplates/conf.py Normal file
View file

@ -0,0 +1,42 @@
import posixpath
from django.core.exceptions import ImproperlyConfigured
from dbtemplates.utils.settings import AppSettings
class DbTemplatesSettings(AppSettings):
USE_CODEMIRROR = False
USE_REVERSION = False
ADD_DEFAULT_SITE = True
AUTO_POPULATE_CONTENT = True
MEDIA_PREFIX = None
CACHE_BACKEND = None
def configure_media_prefix(self, value):
if value is None:
base_url = getattr(self, "STATIC_URL", None)
if base_url is None:
base_url = self.MEDIA_URL
value = posixpath.join(base_url, "dbtemplates/")
return value
def configure_cache_backend(self, value):
# If we are on Django 1.3 AND using the new CACHES setting..
if hasattr(self, "CACHES"):
if "dbtemplates" in self.CACHES:
return "dbtemplates"
else:
return "default"
if isinstance(value, basestring) and value.startswith("dbtemplates."):
raise ImproperlyConfigured("Please upgrade to one of the supported "
"backends as defined in the Django docs.")
return value
def configure_use_reversion(self, value):
if value and 'reversion' not in self.INSTALLED_APPS:
raise ImproperlyConfigured("Please add 'reversion' to your "
"INSTALLED_APPS setting to make use of it in dbtemplates.")
return value
settings = DbTemplatesSettings("DBTEMPLATES")

View file

@ -1,11 +1,11 @@
import os
from optparse import make_option
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.management.base import CommandError, NoArgsCommand
from django.template.loaders.app_directories import app_template_dirs
from dbtemplates.conf import settings
from dbtemplates.models import Template
ALWAYS_ASK, FILES_TO_DATABASE, DATABASE_TO_FILES = ('0', '1', '2')

View file

@ -9,9 +9,10 @@ from django.utils.translation import gettext_lazy as _
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from dbtemplates import settings
from dbtemplates.utils import (add_default_site, add_template_to_cache,
remove_cached_template, get_template_source)
from dbtemplates.conf import settings
from dbtemplates.utils.cache import add_template_to_cache, remove_cached_template
from dbtemplates.utils.template import get_template_source
class Template(models.Model):
@ -22,8 +23,8 @@ class Template(models.Model):
name = models.CharField(_('name'), max_length=100,
help_text=_("Example: 'flatpages/default.html'"))
content = models.TextField(_('content'), blank=True)
sites = models.ManyToManyField(Site, verbose_name=_('sites'), blank=True,
null=True)
sites = models.ManyToManyField(Site, verbose_name=_('sites'),
blank=True, null=True)
creation_date = models.DateTimeField(_('creation date'),
default=datetime.now)
last_changed = models.DateTimeField(_('last changed'),
@ -59,11 +60,24 @@ class Template(models.Model):
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 settings.AUTO_POPULATE_CONTENT and not self.content:
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content:
self.populate()
super(Template, self).save(*args, **kwargs)
def add_default_site(instance, **kwargs):
"""
Called via Django's signals to cache the templates, if the template
in the database was added or changed, only if
DBTEMPLATES_ADD_DEFAULT_SITE setting is set.
"""
if not settings.DBTEMPLATES_ADD_DEFAULT_SITE:
return
current_site = Site.objects.get_current()
if current_site not in instance.sites.all():
instance.sites.add(current_site)
signals.post_save.connect(add_default_site, sender=Template)
signals.post_save.connect(add_template_to_cache, sender=Template)
signals.pre_delete.connect(remove_cached_template, sender=Template)

View file

@ -1,33 +0,0 @@
import posixpath
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
if "dbtemplates" in getattr(settings, "CACHES", {}):
# If we are on Django 1.3 AND using the new CACHES setting..
cache = "dbtemplates"
else:
# ..or fall back to the old CACHE_BACKEND setting
cache = getattr(settings, "DBTEMPLATES_CACHE_BACKEND", None)
if isinstance(cache, basestring) and cache.startswith("dbtemplates."):
raise ImproperlyConfigured("Please upgrade to one of the supported "
"backends as defined in the Django docs.")
CACHE_BACKEND = cache
ADD_DEFAULT_SITE = getattr(settings, 'DBTEMPLATES_ADD_DEFAULT_SITE', True)
AUTO_POPULATE_CONTENT = getattr(
settings, 'DBTEMPLATES_AUTO_POPULATE_CONTENT', True)
base_url = getattr(settings, "STATIC_URL", None)
if base_url is None:
base_url = settings.MEDIA_URL
MEDIA_PREFIX = getattr(settings, 'DBTEMPLATES_MEDIA_PREFIX',
posixpath.join(base_url, "dbtemplates/"))
USE_REVERSION = getattr(settings, 'DBTEMPLATES_USE_REVERSION', False)
if USE_REVERSION and 'reversion' not in settings.INSTALLED_APPS:
raise ImproperlyConfigured("Please add reversion to your "
"INSTALLED_APPS setting to make use of it in dbtemplates.")
USE_CODEMIRROR = getattr(settings, 'DBTEMPLATES_USE_CODEMIRROR', False)

View file

@ -1,65 +1,49 @@
from django import VERSION
from __future__ import with_statement
from django.core.management import call_command
from django.template import loader, Context
from django.test import TestCase
from django.contrib.sites.models import Site
from dbtemplates import settings
from dbtemplates.loader import load_template_source
from dbtemplates.conf import settings
from dbtemplates.models import Template
from dbtemplates.utils import get_template_source
from dbtemplates.utils.template import get_template_source
class DbTemplatesTestCase(TestCase):
def setUp(self):
self.site1, created1 = Site.objects.get_or_create(domain="example.com", name="example.com")
self.site2, created2 = Site.objects.get_or_create(domain="example.org", name="example.org")
self.t1, _ = Template.objects.get_or_create(name='base.html', content='base')
self.t2, _ = Template.objects.get_or_create(name='sub.html', content='sub')
self.t2.sites.add(self.site2)
def test_basiscs(self):
t1 = Template(name='base.html', content='<html><head></head><body>{% block content %}Welcome at {{ title }}{% endblock %}</body></html>')
t1.save()
self.assertEqual(Site.objects.get_current(), t1.sites.all()[0])
self.assertTrue("Welcome at" in t1.content)
self.assertEqual(list(self.t1.sites.all()), [self.site1])
self.assertTrue("base" in self.t1.content)
self.assertEqual(list(Template.objects.filter(sites=self.site1)), [self.t1, self.t2])
self.assertEqual(list(self.t2.sites.all()), [self.site1, self.site2])
t2 = Template(name='sub.html', content='{% extends "base.html" %}{% block content %}This is {{ title }}{% endblock %}')
t2.save()
t2.sites.add(self.site2)
self.assertEqual(list(Template.objects.filter(sites=self.site1)), [t1, t2])
self.assertEqual(list(t2.sites.all()), [self.site1, self.site2])
def test_empty_sites(self):
old_add_default_site = settings.DBTEMPLATES_ADD_DEFAULT_SITE
try:
settings.DBTEMPLATES_ADD_DEFAULT_SITE = False
self.t3 = Template.objects.create(name='footer.html', content='footer')
self.assertEqual(list(self.t3.sites.all()), [])
finally:
settings.DBTEMPLATES_ADD_DEFAULT_SITE = old_add_default_site
def test_load_templates(self):
self.test_basiscs()
original_template_source_loaders = loader.template_source_loaders
loader.template_source_loaders = [load_template_source]
try:
result1 = loader.get_template("base.html").render(Context({'title':'MainPage'}))
self.assertEqual(result1, u'<html><head></head><body>Welcome at MainPage</body></html>')
result2 = loader.get_template("sub.html").render(Context({'title':'SubPage'}))
self.assertEqual(result2, u'<html><head></head><body>This is SubPage</body></html>')
if VERSION[:2] >= (1, 2):
from dbtemplates.loader import Loader
dbloader = Loader()
loader.template_source_loaders = [dbloader.load_template_source]
result = loader.get_template("base.html").render(Context({'title':'MainPage'}))
self.assertEqual(result, u'<html><head></head><body>Welcome at MainPage</body></html>')
result2 = loader.get_template("sub.html").render(Context({'title':'SubPage'}))
self.assertEqual(result2, u'<html><head></head><body>This is SubPage</body></html>')
finally:
loader.template_source_loaders = original_template_source_loaders
result = loader.get_template("base.html").render(Context({}))
self.assertEqual(result, 'base')
result2 = loader.get_template("sub.html").render(Context({}))
self.assertEqual(result2, 'sub')
def test_error_templates_creation(self):
call_command('create_error_templates', force=True, verbosity=0)
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
list(Template.objects.filter()))
def test_disabling_default_site(self):
old_add_default_site = settings.ADD_DEFAULT_SITE
settings.ADD_DEFAULT_SITE = False
t3 = Template.objects.create(name='footer.html', content='ohai')
self.assertEqual(list(t3.sites.all()), [])
settings.ADD_DEFAULT_SITE = old_add_default_site
list(Template.objects.filter()))
def test_automatic_sync(self):
admin_base_template = get_template_source('admin/base.html')

View file

@ -16,7 +16,7 @@ def get_cache_key(name):
return 'dbtemplates::%s::%s' % (name, current_site.pk)
def set_and_return(content):
def set_and_return(content, display_name):
# Save in cache backend explicitly if manually deleted or invalidated
if cache:
cache.set(cache_key, content)

View file

@ -0,0 +1,105 @@
from inspect import getmembers
from django.conf import settings
class AppSettings(object):
"""
An app setting object to be used for handling app setting defaults
gracefully and providing a nice API for them. Say you have an app
called ``myapp`` and want to define a few defaults, and refer to the
defaults easily in the apps code. Add a ``settings.py`` to your app::
from path.to.utils import AppSettings
class MyAppSettings(AppSettings):
SETTING_1 = "one"
SETTING_2 = (
"two",
)
Then initialize the setting with the correct prefix in the location of
of your choice, e.g. ``conf.py`` of the app module::
settings = MyAppSettings(prefix="MYAPP")
The ``MyAppSettings`` instance will automatically look at Django's
global setting to determine each of the settings and respect the
provided ``prefix``. E.g. adding this to your site's ``settings.py``
will set the ``SETTING_1`` setting accordingly::
MYAPP_SETTING_1 = "uno"
Usage
-----
Instead of using ``from django.conf import settings`` as you would
usually do, you can switch to using your apps own settings module
to access the app settings::
from myapp.conf import settings
print myapp_settings.MYAPP_SETTING_1
``AppSettings`` instances also work as pass-throughs for other
global settings that aren't related to the app. For example the
following code is perfectly valid::
from myapp.conf import settings
if "myapp" in settings.INSTALLED_APPS:
print "yay, myapp is installed!"
Custom handling
---------------
Each of the settings can be individually configured with callbacks.
For example, in case a value of a setting depends on other settings
or other dependencies. The following example sets one setting to a
different value depending on a global setting::
from django.conf import settings
class MyCustomAppSettings(AppSettings):
ENABLED = True
def configure_enabled(self, value):
return value and not self.DEBUG
custom_settings = MyCustomAppSettings("MYAPP")
The value of ``custom_settings.MYAPP_ENABLED`` will vary depending on the
value of the global ``DEBUG`` setting.
Each of the app settings can be customized by providing
a method ``configure_<lower_setting_name>`` that takes the default
value as defined in the class attributes as the only parameter.
The method needs to return the value to be use for the setting in
question.
"""
def __dir__(self):
return sorted(list(set(self.__dict__.keys() + dir(settings))))
__members__ = lambda self: self.__dir__()
def __getattr__(self, name):
if name.startswith(self._prefix):
raise AttributeError("%r object has no attribute %r" %
(self.__class__.__name__, name))
return getattr(settings, name)
def __setattr__(self, name, value):
super(AppSettings, self).__setattr__(name, value)
if name in dir(settings):
setattr(settings, name, value)
def __init__(self, prefix):
super(AppSettings, self).__setattr__('_prefix', prefix)
for setting, class_value in getmembers(self.__class__):
if setting == setting.upper():
prefixed = "%s_%s" % (prefix.upper(), setting.upper())
configured_value = getattr(settings, prefixed, class_value)
callback = getattr(self, "configure_%s" % setting.lower(), None)
if callable(callback):
configured_value = callback(configured_value)
delattr(self.__class__, setting)
setattr(self, prefixed, configured_value)

View file

@ -15,6 +15,11 @@ if not settings.configured:
'django.contrib.admin',
'dbtemplates',
],
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
'dbtemplates.loader.Loader',
)
)
from django.test.simple import run_tests