template_engines/backends/__init__.py
import io
import os
import zipfile
from zipfile import BadZipFile
from bs4 import BeautifulSoup
from django.core.files.storage import default_storage
from django.template.exceptions import TemplateDoesNotExist
from django.template.backends.django import DjangoTemplates
from django.utils.functional import cached_property
from template_engines.utils import clean_tags
class AbstractTemplate:
"""
Gives the architecture of a basic template.
"""
def __init__(self, template):
self.template = template
def clean(self, data):
"""
Method for cleaning rendered data.
"""
raise NotImplementedError()
def render(self, context=None, request=None):
"""
Fills a template with the context obtained by combining the `context` and` request`
parameters and returns a file as a byte object.
"""
raise NotImplementedError()
class BaseEngine(DjangoTemplates):
app_dirname = None
sub_dirname = None
template_class = None
@cached_property
def template_dirs(self):
t_dirs = super().template_dirs
if self.sub_dirname:
t_dirs += tuple([os.path.join(p, self.sub_dirname) for p in t_dirs])
return t_dirs
class ZipAbstractEngine(BaseEngine):
"""
Gives the architecture of a basic zip base template engine.
Can be specified:
* ``app_dirname``, the folder name which contains the templates in application directories,
* ``sub_dirname``, the folder name of the subdirectory in the templates directory,
* ``template_class``, your own template class with a ``render`` method,
* ``zip_root_file``, the file to fill.
"""
zip_root_files = None
def get_template_content(self, filename):
"""
Returns the contents of a template before modification, as a string.
"""
try:
with default_storage.open(filename, 'rb') as template_file:
template_buffer = io.BytesIO(template_file.read())
with zipfile.ZipFile(template_buffer, 'r') as zip_file:
soup = BeautifulSoup('', 'html.parser')
global_tag = soup.new_tag("global-merged")
for file_to_merge in self.zip_root_files:
name, _ = os.path.splitext(file_to_merge)
content = zip_file.read(file_to_merge).decode()
content_soup = BeautifulSoup(content, 'xml')
tag_file = content_soup.new_tag('{0}-merged'.format(name.replace('/', '-')))
tag_file.append(content_soup)
global_tag.append(tag_file)
soup.append(global_tag)
return str(soup)
except (KeyError, BadZipFile):
raise TemplateDoesNotExist('Bad format.')
def from_string(self, template_code, **kwargs):
return self.template_class(self.engine.from_string(template_code), **kwargs)
def get_template_path(self, filename):
"""
Check if a template named ``template_name`` can be found in a list of directories. Returns
the path if the file exists or raises ``TemplateDoesNotExist`` otherwise.
"""
if default_storage.exists(filename):
return filename
for directory in self.template_dirs:
abstract_path = default_storage.generate_filename(os.path.join(directory, filename))
if default_storage.exists(abstract_path):
return abstract_path
raise TemplateDoesNotExist(f'Unknown: {filename}')
def clean_content(self, content):
return clean_tags(content)
def get_template(self, template_name):
template_path = self.get_template_path(template_name)
content = self.get_template_content(template_path)
content = self.clean_content(content)
return self.from_string(content, template_path=template_path)