Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for django2+ on templates #608

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
Empty file.
37 changes: 37 additions & 0 deletions tenant_schemas/template/loaders/cached.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Wrapper class that takes a list of template loaders as an argument and attempts
to load templates from them in order, caching the result, in a multi-tenant setting.
"""

from django.db import connection

from django.template.loaders.cached import Loader as BaseLoader


class Loader(BaseLoader):

def cache_key(self, template_name, skip=None):
"""
Generate a cache key for the template name, dirs, and skip.
If skip is provided, only origins that match template_name are included
in the cache key. This ensures each template is only parsed and cached
once if contained in different extend chains like:
x -> a -> a
y -> a -> a
z -> a -> a
"""
dirs_prefix = ''
skip_prefix = ''
tenant_prefix = ''

if skip:
matching = [
origin.name for origin in skip if origin.template_name == template_name]

if matching:
skip_prefix = self.generate_hash(matching)

if connection.tenant:
tenant_prefix = str(connection.tenant.pk)

return '-'.join(s for s in (str(template_name), tenant_prefix, skip_prefix, dirs_prefix) if s)
48 changes: 48 additions & 0 deletions tenant_schemas/template/loaders/filesystem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Wrapper for loading templates from the filesystem in a multi-tenant setting.
"""

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db import connection
from django.template.loaders.filesystem import Loader as BaseLoader

from tenant_schemas import utils


class Loader(BaseLoader):
def __init__(self, engine, dirs=None):
self._dirs = {}

super().__init__(engine)

if dirs is not None:
self.dirs = dirs

@property
def dirs(self):
"""
Lazy retrieval of list of template directories based on current tenant schema.
:return: The list of template file dirs that have been configured for this tenant.
"""
if self._dirs.get(connection.schema_name, None) is None:
try:
# Use directories configured via MULTITENANT_TEMPLATE_DIRS
dirs = [
utils.parse_tenant_config_path(dir_)
for dir_ in settings.MULTITENANT_TEMPLATE_DIRS
]
except AttributeError:
raise ImproperlyConfigured(
"To use {}.{} you must define the MULTITENANT_TEMPLATE_DIRS setting.".format(
__name__, Loader.__name__
)
)

self.dirs = dirs

return self._dirs[connection.schema_name]

@dirs.setter
def dirs(self, value):
self._dirs[connection.schema_name] = value
18 changes: 18 additions & 0 deletions tenant_schemas/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,21 @@ def app_labels(apps_list):
if AppConfig is None:
return [app.split('.')[-1] for app in apps_list]
return [AppConfig.create(app).label for app in apps_list]


def parse_tenant_config_path(config_path):
"""
Convenience function for parsing django-tenants' path configuration strings.
If the string contains '%s', then the current tenant's schema name will be inserted at that location. Otherwise
the schema name will be appended to the end of the string.
:param config_path: A configuration path string that optionally contains '%s' to indicate where the tenant
schema name should be inserted.
:return: The formatted string containing the schema name
"""
try:
# Insert schema name
print("schema -> %s" % connection.schema_name)
return config_path % connection.schema_name
except (TypeError, ValueError):
# No %s in string; append schema name at the end
return os.path.join(config_path, connection.schema_name)