diff --git a/dbtemplates/loader.py b/dbtemplates/loader.py index 5a57f27..243da31 100644 --- a/dbtemplates/loader.py +++ b/dbtemplates/loader.py @@ -52,23 +52,24 @@ class Loader(BaseLoader): # timestamp. site = Site.objects.get_current() cache_key = get_cache_key(template_name) - if cache: - try: - backend_template = cache.get(cache_key) - if backend_template: - return backend_template, template_name - except Exception: - pass - # Not found in cache, move on. cache_notfound_key = get_cache_notfound_key(template_name) if cache: + try: + backend_template = cache.get(cache_key) + except Exception: + pass + else: + if backend_template: + return (backend_template, template_name) + try: notfound = cache.get(cache_notfound_key) - if notfound: - raise TemplateDoesNotExist(template_name) except Exception: raise TemplateDoesNotExist(template_name) + else: + if notfound: + raise TemplateDoesNotExist(template_name) # Not marked as not-found, move on... diff --git a/dbtemplates/management/commands/sync_templates.py b/dbtemplates/management/commands/sync_templates.py index 7e336e0..6ce7fdc 100644 --- a/dbtemplates/management/commands/sync_templates.py +++ b/dbtemplates/management/commands/sync_templates.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path from dbtemplates.models import Template from django.contrib.sites.models import Site @@ -86,19 +86,17 @@ class Command(BaseCommand): tpl_dirs = app_template_dirs + DIRS else: tpl_dirs = DIRS + app_template_dirs - templatedirs = [str(d) for d in tpl_dirs if os.path.isdir(d)] + templatedirs = [Path(d) for d in tpl_dirs if Path(d).is_dir()] for templatedir in templatedirs: - for dirpath, subdirs, filenames in os.walk(templatedir): + for dirpath, subdirs, filenames in templatedir.walk(follow_symlinks=True): for f in [ f for f in filenames if f.endswith(extension) and not f.startswith(".") ]: - path = os.path.join(dirpath, f) - name = path.split(str(templatedir))[1] - if name.startswith("/"): - name = name[1:] + path = dirpath / f + name = path.relative_to(templatedir) try: t = Template.on_site.get(name__exact=name) except Template.DoesNotExist: @@ -110,9 +108,7 @@ class Command(BaseCommand): "" % (name, path) ) if force or confirm.lower().startswith("y"): - with open(path, encoding="utf-8") as f: - t = Template(name=name, content=f.read()) - t.save() + t = Template.objects.create(name=name, content=path.read_text(encoding="utf-8")) t.sites.add(site) else: while True: @@ -134,20 +130,18 @@ class Command(BaseCommand): DATABASE_TO_FILES, ): if confirm == FILES_TO_DATABASE: - with open(path, encoding="utf-8") as f: - t.content = f.read() - t.save() - t.sites.add(site) + t.content = path.read_text(encoding="utf-8") + t.save() + t.sites.add(site) if delete: try: - os.remove(path) + path.unlink(missing_ok=True) except OSError: raise CommandError( f"Couldn't delete {path}" ) elif confirm == DATABASE_TO_FILES: - with open(path, "w", encoding="utf-8") as f: # noqa - f.write(t.content) + path.write_text(t.content, encoding="utf-8") if delete: t.delete() break diff --git a/dbtemplates/models.py b/dbtemplates/models.py index 6a9d35d..e84ecd3 100644 --- a/dbtemplates/models.py +++ b/dbtemplates/models.py @@ -49,10 +49,10 @@ class Template(models.Model): name = self.name try: source = get_template_source(name) - if source: - self.content = source except TemplateDoesNotExist: pass + else: + self.content = source def add_default_site(instance, **kwargs): diff --git a/dbtemplates/test_cases.py b/dbtemplates/test_cases.py index d29f376..acd0cf1 100644 --- a/dbtemplates/test_cases.py +++ b/dbtemplates/test_cases.py @@ -1,11 +1,9 @@ -import os import shutil import tempfile from contextlib import contextmanager from pathlib import Path from unittest import mock -from django.conf import settings as django_settings from django.core.cache.backends.base import BaseCache from django.core.management import call_command from django.db.models.signals import post_save @@ -16,12 +14,12 @@ from django.test.signals import receiver, setting_changed 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 get_cache_backend, get_cache_key +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.sync_templates import (FILES_TO_DATABASE, - DATABASE_TO_FILES) +from dbtemplates.management.commands import sync_templates @receiver(setting_changed) @@ -43,7 +41,15 @@ def temptemplate(name: str, cleanup: bool = True): shutil.rmtree(temp_template_dir) -class DbTemplatesTestCase(TestCase): +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") + self.assertEqual(rtn, ("cache test content", "cache display name")) + self.assertEqual(cache.get("this_is_the_cache_key"), "cache test content") + + +class BaseDbTemplatesTestCase(TestCase): @modify_settings( TEMPLATES={ "append": "dbtemplates.loader.Loader", @@ -60,18 +66,15 @@ class DbTemplatesTestCase(TestCase): name='sub.html', content='sub') self.t2.sites.add(self.site2) - def test_basics(self): - self.assertQuerySetEqual(self.t1.sites.all(), Site.objects.filter(id=self.site1.id)) - self.assertIn("base", self.t1.content) - 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])) - @override_settings(DBTEMPLATES_ADD_DEFAULT_SITE=False) - def test_empty_sites(self): - self.t3 = Template.objects.create( - name='footer.html', content='footer') - self.assertQuerySetEqual(self.t3.sites.all(), self.t3.sites.none()) +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')) @override_settings(DBTEMPLATES_ADD_DEFAULT_SITE=False) def test_load_templates_sites(self): @@ -96,67 +99,6 @@ class DbTemplatesTestCase(TestCase): result2 = loader.get_template("sub.html").render() self.assertEqual(result2, 'sub') - 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()) - - def test_automatic_sync(self): - 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_sync_templates(self): - old_template_dirs = settings.TEMPLATES[0].get('DIRS', []) - with temptemplate('temp_test.html') as temp_template_path: - with open(temp_template_path, 'w', encoding='utf-8') as temp_template: - temp_template.write('temp test') - try: - settings.TEMPLATES[0]['DIRS'] = (temp_template_path.parent,) - # these works well if is not settings patched at runtime - # for supporting django < 1.7 tests we must patch dirs in runtime - from dbtemplates.management.commands import sync_templates - sync_templates.DIRS = settings.TEMPLATES[0]['DIRS'] - - self.assertFalse( - Template.objects.filter(name='temp_test.html').exists()) - call_command('sync_templates', force=True, - verbosity=0, overwrite=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.save() - call_command('sync_templates', force=True, - verbosity=0, overwrite=DATABASE_TO_FILES) - with open(temp_template_path, encoding='utf-8') as f: - self.assertEqual('temp test modified', f.read()) - - call_command('sync_templates', force=True, verbosity=0, - delete=True, overwrite=DATABASE_TO_FILES) - self.assertTrue(os.path.exists(temp_template_path)) - self.assertFalse( - Template.objects.filter(name='temp_test.html').exists()) - finally: - settings.TEMPLATES[0]['DIRS'] = old_template_dirs - - def test_get_cache(self): - self.assertTrue(isinstance(get_cache_backend(), BaseCache)) - - def test_check_template_syntax(self): - bad_template, _ = Template.objects.get_or_create( - name='bad.html', content='{% if foo %}Bar') - good_template, _ = Template.objects.get_or_create( - 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') - def test_cache_invalidation(self): # Add t1 into the cache of site2 self.t1.sites.add(self.site2) @@ -177,3 +119,97 @@ class DbTemplatesTestCase(TestCase): return_value=self.site2): result = loader.get_template("base.html").render() self.assertEqual(result, 'new content') + + +class DbTemplatesModelsTestCase(BaseDbTemplatesTestCase): + def test_basics(self): + 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])) + + def test_populate(self): + t0 = Template.objects.create(name='header.html', content='