Format with ruff

This commit is contained in:
blag 2025-06-10 22:17:05 -06:00
parent 2c8e80bc6e
commit 58e88b1f18
No known key found for this signature in database
15 changed files with 367 additions and 263 deletions

View file

@ -14,8 +14,7 @@ from dbtemplates.utils.template import check_template_syntax
# use reversion_compare's CompareVersionAdmin or reversion's VersionAdmin as
# the base admin class if yes
if settings.DBTEMPLATES_USE_REVERSION_COMPARE:
from reversion_compare.admin import CompareVersionAdmin \
as TemplateModelAdmin
from reversion_compare.admin import CompareVersionAdmin as TemplateModelAdmin
elif settings.DBTEMPLATES_USE_REVERSION:
from reversion.admin import VersionAdmin as TemplateModelAdmin
else:
@ -23,22 +22,22 @@ else:
class CodeMirrorTextArea(forms.Textarea):
"""
A custom widget for the CodeMirror browser editor to be used with the
content field of the Template model.
"""
class Media:
css = dict(screen=[posixpath.join(
settings.DBTEMPLATES_MEDIA_PREFIX, 'css/editor.css')])
js = [posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX,
'js/codemirror.js')]
css = dict(
screen=[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, renderer=None):
result = []
result.append(super().render(name, value, attrs))
result.append(
super().render(name, value, attrs))
result.append("""
"""
<script type="text/javascript">
var editor = CodeMirror.fromTextArea('id_%(name)s', {
path: "%(media_prefix)sjs/",
@ -51,7 +50,9 @@ class CodeMirrorTextArea(forms.Textarea):
lineNumbers: true
});
</script>
""" % dict(media_prefix=settings.DBTEMPLATES_MEDIA_PREFIX, name=name))
"""
% dict(media_prefix=settings.DBTEMPLATES_MEDIA_PREFIX, name=name)
)
return mark_safe("".join(result))
@ -61,62 +62,79 @@ else:
TemplateContentTextArea = forms.Textarea
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.")
content_help_text = _(
"Leaving this empty causes Django to look for a "
"template with the given name and populate this "
"field with its content."
)
else:
content_help_text = ""
if settings.DBTEMPLATES_USE_CODEMIRROR and settings.DBTEMPLATES_USE_TINYMCE:
raise ImproperlyConfigured("You may use either CodeMirror or TinyMCE "
"with dbtemplates, not both. Please disable "
"one of them.")
raise ImproperlyConfigured(
"You may use either CodeMirror or TinyMCE "
"with dbtemplates, not both. Please disable "
"one of them."
)
if settings.DBTEMPLATES_USE_TINYMCE:
from tinymce.widgets import AdminTinyMCE
TemplateContentTextArea = AdminTinyMCE
elif settings.DBTEMPLATES_USE_REDACTOR:
from redactor.widgets import RedactorEditor
TemplateContentTextArea = RedactorEditor
class TemplateAdminForm(forms.ModelForm):
"""
Custom AdminForm to make the content textarea wider.
"""
content = forms.CharField(
widget=TemplateContentTextArea(attrs={'rows': '24'}),
help_text=content_help_text, required=False)
widget=TemplateContentTextArea(attrs={"rows": "24"}),
help_text=content_help_text,
required=False,
)
class Meta:
model = Template
fields = ('name', 'content', 'sites', 'creation_date', 'last_changed')
fields = ("name", "content", "sites", "creation_date", "last_changed")
fields = "__all__"
class TemplateAdmin(TemplateModelAdmin):
form = TemplateAdminForm
readonly_fields = ['creation_date', 'last_changed']
readonly_fields = ["creation_date", "last_changed"]
fieldsets = (
(None, {
'fields': ('name', 'content'),
'classes': ('monospace',),
}),
(_('Advanced'), {
'fields': (('sites'),),
}),
(_('Date/time'), {
'fields': (('creation_date', 'last_changed'),),
'classes': ('collapse',),
}),
(
None,
{
"fields": ("name", "content"),
"classes": ("monospace",),
},
),
(
_("Advanced"),
{
"fields": (("sites"),),
},
),
(
_("Date/time"),
{
"fields": (("creation_date", "last_changed"),),
"classes": ("collapse",),
},
),
)
filter_horizontal = ('sites',)
list_display = ('name', 'creation_date', 'last_changed', 'site_list')
list_filter = ('sites',)
filter_horizontal = ("sites",)
list_display = ("name", "creation_date", "last_changed", "site_list")
list_filter = ("sites",)
save_as = True
search_fields = ('name', 'content')
actions = ['invalidate_cache', 'repopulate_cache', 'check_syntax']
search_fields = ("name", "content")
actions = ["invalidate_cache", "repopulate_cache", "check_syntax"]
def invalidate_cache(self, request, queryset):
for template in queryset:
@ -125,10 +143,11 @@ class TemplateAdmin(TemplateModelAdmin):
message = ngettext(
"Cache of one template successfully invalidated.",
"Cache of %(count)d templates successfully invalidated.",
count)
self.message_user(request, message % {'count': count})
invalidate_cache.short_description = _("Invalidate cache of "
"selected templates")
count,
)
self.message_user(request, message % {"count": count})
invalidate_cache.short_description = _("Invalidate cache of selected templates")
def repopulate_cache(self, request, queryset):
for template in queryset:
@ -137,37 +156,43 @@ class TemplateAdmin(TemplateModelAdmin):
message = ngettext(
"Cache successfully repopulated with one template.",
"Cache successfully repopulated with %(count)d templates.",
count)
self.message_user(request, message % {'count': count})
repopulate_cache.short_description = _("Repopulate cache with "
"selected templates")
count,
)
self.message_user(request, message % {"count": count})
repopulate_cache.short_description = _("Repopulate cache with selected templates")
def check_syntax(self, request, queryset):
errors = []
for template in queryset:
valid, error = check_template_syntax(template)
if not valid:
errors.append(f'{template.name}: {error}')
errors.append(f"{template.name}: {error}")
if errors:
count = len(errors)
message = ngettext(
"Template syntax check FAILED for %(names)s.",
"Template syntax check FAILED for "
"%(count)d templates: %(names)s.",
count)
self.message_user(request, message %
{'count': count, 'names': ', '.join(errors)})
"Template syntax check FAILED for %(count)d templates: %(names)s.",
count,
)
self.message_user(
request, message % {"count": count, "names": ", ".join(errors)}
)
else:
count = queryset.count()
message = ngettext(
"Template syntax OK.",
"Template syntax OK for %(count)d templates.", count)
self.message_user(request, message % {'count': count})
"Template syntax OK for %(count)d templates.",
count,
)
self.message_user(request, message % {"count": count})
check_syntax.short_description = _("Check template syntax")
def site_list(self, template):
return ", ".join([site.name for site in template.sites.all()])
site_list.short_description = _('sites')
site_list.short_description = _("sites")
admin.site.register(Template, TemplateAdmin)

View file

@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
class DBTemplatesConfig(AppConfig):
name = 'dbtemplates'
verbose_name = _('Database templates')
name = "dbtemplates"
verbose_name = _("Database templates")
default_auto_field = 'django.db.models.AutoField'
default_auto_field = "django.db.models.AutoField"

View file

@ -33,35 +33,45 @@ class DbTemplatesConf(AppConf):
else:
return "default"
if isinstance(value, str) and value.startswith("dbtemplates."):
raise ImproperlyConfigured("Please upgrade to one of the "
"supported backends as defined "
"in the Django docs.")
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 settings.INSTALLED_APPS:
raise ImproperlyConfigured("Please add 'reversion' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates.")
if value and "reversion" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
"Please add 'reversion' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates."
)
return value
def configure_use_reversion_compare(self, value):
if value and 'reversion_compare' not in settings.INSTALLED_APPS:
raise ImproperlyConfigured("Please add 'reversion_compare' to your"
" INSTALLED_APPS setting to make "
"use of it in dbtemplates.")
if value and "reversion_compare" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
"Please add 'reversion_compare' to your"
" INSTALLED_APPS setting to make "
"use of it in dbtemplates."
)
return value
def configure_use_tinymce(self, value):
if value and 'tinymce' not in settings.INSTALLED_APPS:
raise ImproperlyConfigured("Please add 'tinymce' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates.")
if value and "tinymce" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
"Please add 'tinymce' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates."
)
return value
def configure_use_redactor(self, value):
if value and 'redactor' not in settings.INSTALLED_APPS:
raise ImproperlyConfigured("Please add 'redactor' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates.")
if value and "redactor" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
"Please add 'redactor' to your "
"INSTALLED_APPS setting to make "
"use of it in dbtemplates."
)
return value

View file

@ -4,8 +4,12 @@ from django.template import Origin, TemplateDoesNotExist
from django.template.loaders.base import Loader as BaseLoader
from dbtemplates.models import Template
from dbtemplates.utils.cache import (cache, get_cache_key,
set_and_return, get_cache_notfound_key)
from dbtemplates.utils.cache import (
cache,
get_cache_key,
set_and_return,
get_cache_notfound_key,
)
class Loader(BaseLoader):
@ -17,6 +21,7 @@ class Loader(BaseLoader):
it falls back to query the database field ``name`` with the template path
and ``sites`` with the current site.
"""
is_usable = True
def get_template_sources(self, template_name, template_dirs=None):
@ -30,11 +35,10 @@ class Loader(BaseLoader):
content, _ = self._load_template_source(origin.template_name)
return content
def _load_and_store_template(self, template_name, cache_key, site,
**params):
def _load_and_store_template(self, template_name, cache_key, site, **params):
template = Template.objects.get(name__exact=template_name, **params)
db = router.db_for_read(Template, instance=template)
display_name = f'dbtemplates:{db}:{template_name}:{site.domain}'
display_name = f"dbtemplates:{db}:{template_name}:{site.domain}"
return set_and_return(cache_key, template.content, display_name)
def _load_template_source(self, template_name, template_dirs=None):
@ -74,15 +78,17 @@ class Loader(BaseLoader):
# Not marked as not-found, move on...
try:
return self._load_and_store_template(template_name, cache_key,
site, sites__in=[site.id])
return self._load_and_store_template(
template_name, cache_key, site, sites__in=[site.id]
)
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
try:
return self._load_and_store_template(template_name, cache_key,
site, sites__isnull=True)
return self._load_and_store_template(
template_name, cache_key, site, sites__isnull=True
)
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
pass
# Mark as not-found in cache.
cache.set(cache_notfound_key, '1')
cache.set(cache_notfound_key, "1")
raise TemplateDoesNotExist(template_name)

View file

@ -12,8 +12,9 @@ class Command(BaseCommand):
for template in Template.objects.all():
valid, error = check_template_syntax(template)
if not valid:
errors.append(f'{template.name}: {error}')
errors.append(f"{template.name}: {error}")
if errors:
raise CommandError(
'Some templates contained errors\n%s' % '\n'.join(errors))
self.stdout.write('OK')
"Some templates contained errors\n%s" % "\n".join(errors)
)
self.stdout.write("OK")

View file

@ -29,29 +29,39 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
"-f", "--force", action="store_true", dest="force",
default=False, help="overwrite existing database templates")
"-f",
"--force",
action="store_true",
dest="force",
default=False,
help="overwrite existing database templates",
)
def handle(self, **options):
force = options.get('force')
force = options.get("force")
try:
site = Site.objects.get_current()
except Site.DoesNotExist:
raise CommandError("Please make sure to have the sites contrib "
"app installed and setup with a site object")
raise CommandError(
"Please make sure to have the sites contrib "
"app installed and setup with a site object"
)
verbosity = int(options.get('verbosity', 1))
verbosity = int(options.get("verbosity", 1))
for error_code in (404, 500):
template, created = Template.objects.get_or_create(
name=f"{error_code}.html")
name=f"{error_code}.html"
)
if created or (not created and force):
template.content = TEMPLATES.get(error_code, '')
template.content = TEMPLATES.get(error_code, "")
template.save()
template.sites.add(site)
if verbosity >= 1:
sys.stdout.write("Created database template "
"for %s errors.\n" % error_code)
sys.stdout.write(
"Created database template for %s errors.\n" % error_code
)
else:
if verbosity >= 1:
sys.stderr.write("A template for %s errors "
"already exists.\n" % error_code)
sys.stderr.write(
"A template for %s errors already exists.\n" % error_code
)

View file

@ -108,7 +108,9 @@ class Command(BaseCommand):
"" % (name, path)
)
if force or confirm.lower().startswith("y"):
t = Template.objects.create(name=name, content=path.read_text(encoding="utf-8"))
t = Template.objects.create(
name=name, content=path.read_text(encoding="utf-8")
)
t.sites.add(site)
else:
while True:

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("sites", "0001_initial"),
]

View file

@ -4,20 +4,19 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dbtemplates', '0001_initial'),
("dbtemplates", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name='template',
name='creation_date',
field=models.DateTimeField(auto_now_add=True, verbose_name='creation date'),
model_name="template",
name="creation_date",
field=models.DateTimeField(auto_now_add=True, verbose_name="creation date"),
),
migrations.AlterField(
model_name='template',
name='last_changed',
field=models.DateTimeField(auto_now=True, verbose_name='last changed'),
model_name="template",
name="last_changed",
field=models.DateTimeField(auto_now=True, verbose_name="last changed"),
),
]

View file

@ -18,24 +18,26 @@ class Template(models.Model):
Defines a template model for use with the database template loader.
The field ``name`` is the equivalent to the filename of a static template.
"""
id = models.AutoField(primary_key=True, verbose_name=_('ID'),
serialize=False, auto_created=True)
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)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
last_changed = models.DateTimeField(_('last changed'), auto_now=True)
id = models.AutoField(
primary_key=True, verbose_name=_("ID"), serialize=False, auto_created=True
)
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)
creation_date = models.DateTimeField(_("creation date"), auto_now_add=True)
last_changed = models.DateTimeField(_("last changed"), auto_now=True)
objects = models.Manager()
on_site = CurrentSiteManager('sites')
on_site = CurrentSiteManager("sites")
class Meta:
db_table = 'django_template'
verbose_name = _('template')
verbose_name_plural = _('templates')
ordering = ('name',)
db_table = "django_template"
verbose_name = _("template")
verbose_name_plural = _("templates")
ordering = ("name",)
def __str__(self):
return self.name

View file

@ -16,9 +16,13 @@ from django.contrib.sites.models import Site
from dbtemplates.conf import settings
from dbtemplates.loader import Loader
from dbtemplates.models import Template, add_default_site
from dbtemplates.utils.cache import cache, get_cache_backend, get_cache_key, set_and_return
from dbtemplates.utils.template import (get_template_source,
check_template_syntax)
from dbtemplates.utils.cache import (
cache,
get_cache_backend,
get_cache_key,
set_and_return,
)
from dbtemplates.utils.template import get_template_source, check_template_syntax
from dbtemplates.management.commands import sync_templates
@ -33,7 +37,7 @@ def handle_add_default_site(sender, setting, value, **kwargs):
@contextmanager
def temptemplate(name: str, cleanup: bool = True):
temp_template_dir = Path(tempfile.mkdtemp('dbtemplates'))
temp_template_dir = Path(tempfile.mkdtemp("dbtemplates"))
temp_template_path = temp_template_dir / name
try:
yield temp_template_path
@ -44,7 +48,9 @@ def temptemplate(name: str, cleanup: bool = True):
class DbTemplatesCacheTestCase(TestCase):
def test_set_and_return(self):
self.assertTrue(bool(cache))
rtn = set_and_return("this_is_the_cache_key", "cache test content", "cache display name")
rtn = set_and_return(
"this_is_the_cache_key", "cache test content", "cache display name"
)
self.assertEqual(rtn, ("cache test content", "cache display name"))
self.assertEqual(cache.get("this_is_the_cache_key"), "cache test content")
@ -57,13 +63,13 @@ class BaseDbTemplatesTestCase(TestCase):
)
def setUp(self):
self.site1, created1 = Site.objects.get_or_create(
domain="example.com", name="example.com")
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')
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)
@ -71,33 +77,38 @@ class DbTemplatesLoaderTestCase(BaseDbTemplatesTestCase):
def test_load_and_store_template(self):
from django.template.loader import _engine_list
from django.core.cache import CacheKeyWarning
loader = Loader(_engine_list()[0])
with self.assertWarns(CacheKeyWarning):
rtn = loader._load_and_store_template('base.html', 'base template cache key', self.site1)
self.assertEqual(rtn, ('base', f'dbtemplates:default:base.html:example.com'))
rtn = loader._load_and_store_template(
"base.html", "base template cache key", self.site1
)
self.assertEqual(rtn, ("base", "dbtemplates:default:base.html:example.com"))
@override_settings(DBTEMPLATES_ADD_DEFAULT_SITE=False)
def test_load_templates_sites(self):
t_site1 = Template.objects.create(
name='copyright.html', content='(c) example.com')
name="copyright.html", content="(c) example.com"
)
t_site1.sites.add(self.site1)
t_site2 = Template.objects.create(
name='copyright.html', content='(c) example.org')
name="copyright.html", content="(c) example.org"
)
t_site2.sites.add(self.site2)
new_site = Site.objects.create(
domain="example.net", name="example.net")
new_site = Site.objects.create(domain="example.net", name="example.net")
with self.settings(SITE_ID=new_site.id):
Site.objects.clear_cache()
self.assertRaises(TemplateDoesNotExist,
loader.get_template, "copyright.html")
self.assertRaises(
TemplateDoesNotExist, loader.get_template, "copyright.html"
)
def test_load_templates(self):
result = loader.get_template("base.html").render()
self.assertEqual(result, 'base')
self.assertEqual(result, "base")
result2 = loader.get_template("sub.html").render()
self.assertEqual(result2, 'sub')
self.assertEqual(result2, "sub")
def test_cache_invalidation(self):
# Add t1 into the cache of site2
@ -123,52 +134,61 @@ class DbTemplatesLoaderTestCase(BaseDbTemplatesTestCase):
class DbTemplatesModelsTestCase(BaseDbTemplatesTestCase):
def test_basics(self):
self.assertQuerySetEqual(self.t1.sites.all(), Site.objects.filter(id=self.site1.id))
self.assertQuerySetEqual(
self.t1.sites.all(), Site.objects.filter(id=self.site1.id)
)
self.assertIn("base", self.t1.content)
self.assertEqual(str(self.t1), self.t1.name)
self.assertEqual(str(self.t2), self.t2.name)
self.assertQuerySetEqual(Template.objects.filter(sites=self.site1),
Template.objects.filter(id__in=[self.t1.id, self.t2.id]))
self.assertQuerySetEqual(self.t2.sites.all(), Site.objects.filter(id__in=[self.site1.id, self.site2.id]))
self.assertQuerySetEqual(
Template.objects.filter(sites=self.site1),
Template.objects.filter(id__in=[self.t1.id, self.t2.id]),
)
self.assertQuerySetEqual(
self.t2.sites.all(),
Site.objects.filter(id__in=[self.site1.id, self.site2.id]),
)
def test_populate(self):
t0 = Template.objects.create(name='header.html', content='<h1>This is a header</h1>')
t0 = Template.objects.create(
name="header.html", content="<h1>This is a header</h1>"
)
t0.populate()
self.assertEqual(t0.content, "<h1>This is a header</h1>")
t0.populate(name='header.html')
t0.populate(name="header.html")
self.assertEqual(t0.content, "<h1>This is a header</h1>")
with temptemplate('temp_test.html') as temp_template_path:
temp_template_path.write_text('temp test')
(temp_template_path.parent / 'temp_test_2.html').write_text('temp test 2')
with temptemplate("temp_test.html") as temp_template_path:
temp_template_path.write_text("temp test")
(temp_template_path.parent / "temp_test_2.html").write_text("temp test 2")
NEW_TEMPLATES = settings.TEMPLATES.copy()
NEW_TEMPLATES[0]['DIRS'] = (temp_template_path.parent,)
NEW_TEMPLATES[0]["DIRS"] = (temp_template_path.parent,)
with self.settings(TEMPLATES=NEW_TEMPLATES):
t1 = Template.objects.create(name='temp_test.html')
t1 = Template.objects.create(name="temp_test.html")
t1.populate()
self.assertEqual(t1.content, "temp test")
t2 = Template.objects.create(name='temp_test.html')
t2.populate(name='temp_test_2.html')
t2 = Template.objects.create(name="temp_test.html")
t2.populate(name="temp_test_2.html")
self.assertEqual(t2.content, "temp test 2")
t3 = Template.objects.create(name='temp_test_3.html')
self.assertIsNone(t3.populate(name='temp_test_doesnt_exist.html'))
t3 = Template.objects.create(name="temp_test_3.html")
self.assertIsNone(t3.populate(name="temp_test_doesnt_exist.html"))
self.assertEqual(t3.content, "")
@override_settings(DBTEMPLATES_ADD_DEFAULT_SITE=False)
def test_empty_sites(self):
self.t3 = Template.objects.create(
name='footer.html', content='footer')
self.t3 = Template.objects.create(name="footer.html", content="footer")
self.assertQuerySetEqual(self.t3.sites.all(), self.t3.sites.none())
def test_error_templates_creation(self):
call_command('create_error_templates', force=True, verbosity=0)
self.assertQuerySetEqual(Template.objects.filter(sites=self.site1),
Template.objects.filter())
self.assertTrue(Template.objects.filter(name='404.html').exists())
call_command("create_error_templates", force=True, verbosity=0)
self.assertQuerySetEqual(
Template.objects.filter(sites=self.site1), Template.objects.filter()
)
self.assertTrue(Template.objects.filter(name="404.html").exists())
def test_automatic_sync(self):
admin_base_template = get_template_source('admin/base.html')
template = Template.objects.create(name='admin/base.html')
admin_base_template = get_template_source("admin/base.html")
template = Template.objects.create(name="admin/base.html")
self.assertEqual(admin_base_template, template.content)
def test_get_cache(self):
@ -176,40 +196,63 @@ class DbTemplatesModelsTestCase(BaseDbTemplatesTestCase):
def test_check_template_syntax(self):
bad_template, _ = Template.objects.get_or_create(
name='bad.html', content='{% if foo %}Bar')
name="bad.html", content="{% if foo %}Bar"
)
good_template, _ = Template.objects.get_or_create(
name='good.html', content='{% if foo %}Bar{% endif %}')
name="good.html", content="{% if foo %}Bar{% endif %}"
)
self.assertFalse(check_template_syntax(bad_template)[0])
self.assertTrue(check_template_syntax(good_template)[0])
def test_get_cache_name(self):
self.assertEqual(get_cache_key('name with spaces'),
'dbtemplates::name-with-spaces::1')
self.assertEqual(
get_cache_key("name with spaces"), "dbtemplates::name-with-spaces::1"
)
class DbTemplatesSyncTemplatesCommandTestCase(TestCase):
def test_sync_templates(self):
with temptemplate('temp_test.html') as temp_template_path:
temp_template_path.write_text('temp test', encoding='utf-8')
with temptemplate("temp_test.html") as temp_template_path:
temp_template_path.write_text("temp test", encoding="utf-8")
NEW_TEMPLATES = settings.TEMPLATES.copy()
NEW_TEMPLATES[0]['DIRS'] = sync_templates.DIRS = (temp_template_path.parent,)
NEW_TEMPLATES[0]["DIRS"] = sync_templates.DIRS = (
temp_template_path.parent,
)
with self.settings(TEMPLATES=NEW_TEMPLATES):
self.assertFalse(
Template.objects.filter(name='temp_test.html').exists())
call_command('sync_templates', force=True,
verbosity=0, overwrite=sync_templates.FILES_TO_DATABASE)
self.assertTrue(
Template.objects.filter(name='temp_test.html').exists())
Template.objects.filter(name="temp_test.html").exists()
)
call_command(
"sync_templates",
force=True,
verbosity=0,
overwrite=sync_templates.FILES_TO_DATABASE,
)
self.assertTrue(Template.objects.filter(name="temp_test.html").exists())
t = Template.objects.get(name='temp_test.html')
t.content = 'temp test modified'
t = Template.objects.get(name="temp_test.html")
t.content = "temp test modified"
t.save()
call_command('sync_templates', force=True,
verbosity=0, overwrite=sync_templates.DATABASE_TO_FILES)
self.assertEqual('temp test modified', temp_template_path.read_text(encoding='utf-8'))
call_command(
"sync_templates",
force=True,
verbosity=0,
overwrite=sync_templates.DATABASE_TO_FILES,
)
self.assertEqual(
"temp test modified", temp_template_path.read_text(encoding="utf-8")
)
call_command('sync_templates', ext='.html', app_first=True, force=True, verbosity=0,
delete=True, overwrite=sync_templates.DATABASE_TO_FILES)
call_command(
"sync_templates",
ext=".html",
app_first=True,
force=True,
verbosity=0,
delete=True,
overwrite=sync_templates.DATABASE_TO_FILES,
)
self.assertTrue(temp_template_path.exists())
self.assertFalse(
Template.objects.filter(name='temp_test.html').exists())
Template.objects.filter(name="temp_test.html").exists()
)

View file

@ -1,10 +1,10 @@
DATABASE_ENGINE = 'sqlite3'
DATABASE_ENGINE = "sqlite3"
# SQLite does not support removing unique constraints (see #28)
SOUTH_TESTS_MIGRATE = False
SITE_ID = 1
SECRET_KEY = 'something-something'
SECRET_KEY = "something-something"
CACHES = {
"default": {
@ -13,43 +13,43 @@ CACHES = {
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
}
INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sites',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.admin',
'django.contrib.auth',
'dbtemplates',
"django.contrib.contenttypes",
"django.contrib.sites",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.admin",
"django.contrib.auth",
"dbtemplates",
]
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
)
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'dbtemplates.loader.Loader',
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
"dbtemplates.loader.Loader",
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {
'loaders': TEMPLATE_LOADERS,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
]
}
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {
"loaders": TEMPLATE_LOADERS,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]

View file

@ -9,6 +9,7 @@ def get_cache_backend():
Compatibilty wrapper for getting Django's cache backend instance
"""
from django.core.cache import caches
cache = caches.create_connection(settings.DBTEMPLATES_CACHE_BACKEND)
# Some caches -- python-memcached in particular -- need to do a cleanup at

View file

@ -1,9 +1,9 @@
from django.template import (Template, TemplateDoesNotExist,
TemplateSyntaxError)
from django.template import Template, TemplateDoesNotExist, TemplateSyntaxError
def get_loaders():
from django.template.loader import _engine_list
loaders = []
for engine in _engine_list():
loaders.extend(engine.engine.template_loaders)
@ -14,7 +14,7 @@ def get_template_source(name):
source = None
not_found = []
for loader in get_loaders():
if loader.__module__.startswith('dbtemplates.'):
if loader.__module__.startswith("dbtemplates."):
# Don't give a damn about dbtemplates' own loader.
continue
for origin in loader.get_template_sources(name):

View file

@ -15,29 +15,29 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('.'))
sys.path.append(os.path.abspath("."))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage']
extensions = ["sphinx.ext.autodoc", "sphinx.ext.coverage"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.txt'
source_suffix = ".txt"
# The encoding of source files.
#source_encoding = 'utf-8'
# source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = 'django-dbtemplates'
copyright = '2007-2019, Jannis Leidel and contributors'
project = "django-dbtemplates"
copyright = "2007-2019, Jannis Leidel and contributors"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -46,61 +46,62 @@ copyright = '2007-2019, Jannis Leidel and contributors'
# The short X.Y version.
try:
from dbtemplates import __version__
# The short X.Y version.
version = '.'.join(__version__.split('.')[:2])
version = ".".join(__version__.split(".")[:2])
# The full version, including alpha/beta/rc tags.
release = __version__
except ImportError:
version = release = 'dev'
version = release = "dev"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
exclude_trees = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = ['_theme']
@ -114,12 +115,12 @@ html_short_title = "django-dbtemplates"
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@ -128,71 +129,76 @@ html_short_title = "django-dbtemplates"
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-dbtemplatesdoc'
htmlhelp_basename = "django-dbtemplatesdoc"
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-dbtemplates.tex', 'django-dbtemplates Documentation',
'Jannis Leidel and contributors', 'manual'),
(
"index",
"django-dbtemplates.tex",
"django-dbtemplates Documentation",
"Jannis Leidel and contributors",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
# latex_use_modindex = True