pax_global_header00006660000000000000000000000064136151736410014521gustar00rootroot0000000000000052 comment=15b5e838b86437d508ffe9c3f2f92146b51f2fff django-jinja-2.6.0/000077500000000000000000000000001361517364100140615ustar00rootroot00000000000000django-jinja-2.6.0/.gitignore000066400000000000000000000002561361517364100160540ustar00rootroot00000000000000*.pyc .*swp build/ dist/ superview.egg-info/ django_superview.egg-info versiontools-*.egg *.egg-info .project .pydevproject .idea .settings /testing/venv doc/index.html .tox django-jinja-2.6.0/.travis.yml000066400000000000000000000012331361517364100161710ustar00rootroot00000000000000language: python sudo: false cache: pip matrix: include: - python: 3.5 env: TOXENV=py35-django111 - python: 3.6 env: TOXENV=py36-django111 - python: 3.5 env: TOXENV=py35-django22 - python: 3.6 env: TOXENV=py36-django22 - python: 3.7 env: TOXENV=py37-django22 - python: 3.8 env: TOXENV=py38-django22 - python: 3.6 env: TOXENV=py36-django30 - python: 3.7 env: TOXENV=py37-django30 - python: 3.8 env: TOXENV=py38-django30 install: - pip install tox script: - tox notifications: email: recipients: - niwi@niwi.nz on_success: change on_failure: change django-jinja-2.6.0/CHANGES.adoc000066400000000000000000000124531361517364100157660ustar00rootroot00000000000000Changelog ========= Version 2.5.0 ------------- - Fix compatibility issues with Django 3.0, minimum version now 1.11. - Dropped support for Python 2.7, 3.4, adding support through 3.8. Version 2.4.2 ------------- - Added `Template.stream` method to use with StreamingHttpResponse. Version 2.4.1 ------------- - Minor improvements on `makemessages`. Version 2.4.0 ------------- - Revert the 2.3.1 change because importing jinja templates from django is not intended feature and that change breaks the django template object signature. Version 2.3.1 ------------- - Add minor fixes allowing creating templatetags that can load jinja2 templates into django templates (refer to issues #94 and #201 for more information). Version 2.3.0 ------------- - Fix compatibility issues with django 1.11 Version 2.2.2 ------------- - Fix many warnings for django 2.0 Version 2.2.1 ------------- - Fix compatibility issues with django 1.9. Version 2.2.0 ------------- - Remove `removetags` filter (making compatible with django 1.10). Version 2.1.3 ------------- - Fix support passing in django template contexts. - Fix template name matching on tests. Version 2.1.2 ------------- - Fix compatibility issues with django 1.8 - Fix unicode decode error on cache tag. Version 2.1.1 ------------- - Improve makemessages command. Version 2.1.0 ------------- - Fix support for django debug toolbar 1.4 - Improve syntax error reporting. - Improve debug instrumentation. Version 2.0.0 ------------- - Remove django < 1.8 compatibility. - Major code refactor for make it now more simpler. Version 1.4.2 ------------- - Minor fix on extensions. Version 1.4.1 ------------- - Add missing import on setup.py of views. Version 1.4.0 ------------- - Add generic views helpers (thanks to @sbutler). - Minor fixes on imports. - Add helper for set the "undefined" parameter in a easy way. - Add a simple way to add extensions from apps. - Fixed bug related to csrf_token. Version 1.3.3 ------------- - Fix django 1.8 compatibilities. - Fix documentation issues. - Remove obsolete code. Version 1.3.2 ------------- - Do not load django < 1.7 setup related settings for django 1.8 backend. - Fix django-debug-toolbar compatibility. Version 1.3.1 ------------- - Fix bug related to doble inclusion of DEFAULT_EXTENSIONS. - Remove the extra django filters extension and document the change. Version 1.3.0 ------------- - Now all builtin filters, and global functions are implemented using jinja2 extensions. - Breaking change: JINJA2_FILTERS_REPLACE_FROM_DJANGO is removed Version 1.2.1 ------------- - Improved JINJA2_LOADER handling for django <= 1.7 - Add documentation entry for JINJA2_LOADER. Version 1.2.0 ------------- - Allow set custom module as translation engine. (by @toshka) Version 1.1.1 ------------- - Fix typos on function names. - Honor Django's `setting_changed` signal to reinitialize the Jinja2 environment. Thanks to @akx Version 1.1.0 ------------- - Code base refactored. - Add django 1.8 support. - Remove `fix_ampersands` filter. - Fix linebreaksbr autoescape bug. - Start using `jinja2.DebugUndefined` when settings.DEBUG is True. Version 1.0.5 ------------- - Fix template loaders order. - Convert documentation to asciidoctor. - Move changelog to separated file. Version 1.0.4 ------------- - Add render_with decorator as replacement for django inclusion_tag. - Reorder how builtin functions/filters are setted making easy overwrite them. Version 1.0.3 ------------- - Add timezone template filters and template global functions: localtime, tz and timezone. Version 1.0.2 ------------- - Fix bug with application loading with django < 1.7 Version 1.0.1 ------------- - Fix bug introduced in previous version on `easy_thumbnails` contrib app. Version 1.0.0 ------------- - Major code cleanup. - Full django 1.7+ support - Add JINJA2_CONSTANTS settings. Version 0.25 ------------ - Enable newstyle gettext by default. - Add settings for easy disable newstyle gettext. Version 0.24 ------------ - Fix django 1.7 warnings on run tests. - Add all rest methods to error views (403, 404, 500). Version 0.23 ------------ - Add settings JINJA2_FILTERS_REPLACE_FROM_DJANGO - Add settings JINJA2_MUTE_URLRESOLVE_EXCEPTIONS - Improvements on cache tag. - Other bugfixes. Version 0.22 ------------ - Change template order selection. - New contrib: subdomains - New contrib: dajax-ice - Documentation fixes. - Minor improvements. Version 0.21 ------------ - Remove obsolete __version__ variable from __init__.py file. - Add bytecode cache with django cache framework support. Version 0.20 ------------ - Introduce backward incompatible change: all contrib apps are renamed (prepened _ on each module name) for avoid name conflicts with the original package. Version 0.19 ------------ - Bugfixes related to autoescape. Version 0.18 ------------ - Test singnal when stream template method is used. Version 0.17 ------------ - Add 4xx/500 django special views. Version 0.16 ------------ - Remove distribute dependency. Version 0.15 ------------ - Put autoescape ON by default. - Add easy_thumbnails contrib app - Add django humanize contrib app Version 0.14 ------------ - Add jinja2 extensions loading by default Version 0.13 ------------ - New intercept method by regex is added. - Documentation improvements. django-jinja-2.6.0/LICENSE000066400000000000000000000027221361517364100150710ustar00rootroot00000000000000Copyright (c) 2012-2013 Andrey Antukh Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-jinja-2.6.0/MANIFEST.in000066400000000000000000000001351361517364100156160ustar00rootroot00000000000000include LICENSE README.rst include django_jinja/contrib/_pipeline/templates/pipeline/*.jinja django-jinja-2.6.0/README.rst000066400000000000000000000014051361517364100155500ustar00rootroot00000000000000django-jinja ============ Simple and nonobstructive jinja2 integration with Django. .. image:: https://img.shields.io/travis/niwinz/django-jinja.svg?style=flat :target: https://travis-ci.org/niwinz/django-jinja .. image:: https://img.shields.io/pypi/v/django-jinja.svg?style=flat :target: https://pypi.python.org/pypi/django-jinja **Documentation:** http://niwinz.github.io/django-jinja/latest/ How to install? --------------- You can install it with pip: .. code-block:: shell pip install django-jinja How to run tests as a developer ------------------------------- Install the Tox automation tool (outside a virtualenv), then .. code-block:: shell tox Tox will create virtualenvs for different interpreter versions and run the test suite. django-jinja-2.6.0/django_jinja/000077500000000000000000000000001361517364100164765ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/__init__.py000066400000000000000000000000761361517364100206120ustar00rootroot00000000000000default_app_config = 'django_jinja.apps.DjangoJinjaAppConfig' django-jinja-2.6.0/django_jinja/apps.py000066400000000000000000000003651361517364100200170ustar00rootroot00000000000000import django from django.apps import AppConfig from django_jinja import base class DjangoJinjaAppConfig(AppConfig): name = "django_jinja" verbose_name = "Django Jinja" def ready(self): base.patch_django_for_autoescape() django-jinja-2.6.0/django_jinja/backend.py000066400000000000000000000276051361517364100204510ustar00rootroot00000000000000""" Since django 1.8.x, django comes with native multiple template engine support. It also comes with jinja2 backend, but it is slightly unflexible, and it does not support by default all django filters and related stuff. This is an implementation of django backend inteface for use django_jinja easy with django 1.8. """ import copy import sys import os import os.path as path import functools from importlib import import_module import jinja2 from django.conf import settings from django.core import signals from django.core.exceptions import ImproperlyConfigured from django.dispatch import receiver from django.middleware import csrf from django.template import RequestContext from django.template import TemplateDoesNotExist from django.template import TemplateSyntaxError from django.template.backends.base import BaseEngine from django.template.backends.utils import csrf_input_lazy from django.template.backends.utils import csrf_token_lazy from django.template.context import BaseContext from django.utils.encoding import smart_text from django.utils.functional import SimpleLazyObject from django.utils.functional import cached_property from django.utils.module_loading import import_string from django.utils.safestring import mark_safe from . import base from . import builtins from . import library from . import utils class Origin(object): """ A container to hold debug information as described in the template API documentation. """ def __init__(self, name, template_name): self.name = name self.template_name = template_name class Template(object): def __init__(self, template, backend): self.template = template self.backend = backend self.name = template.name self.origin = Origin( name=template.filename, template_name=template.name ) def render(self, context=None, request=None): return mark_safe(self._process_template(self.template.render, context, request)) def stream(self, context=None, request=None): return self._process_template(self.template.stream, context, request) def _process_template(self, handler, context=None, request=None): if context is None: context = {} context = base.dict_from_context(context) if request is not None: def _get_val(): token = csrf.get_token(request) if token is None: return 'NOTPROVIDED' else: return smart_text(token) context["request"] = request context["csrf_token"] = SimpleLazyObject(_get_val) # Support for django context processors for processor in self.backend.context_processors: context.update(processor(request)) if self.backend._tmpl_debug: from django.test import signals # Define a "django" like context for emitatet the multi # layered context object. This is mainly for apps like # django-debug-toolbar that are very coupled to django's # internal implementation of context. if not isinstance(context, BaseContext): class CompatibilityContext(dict): @property def dicts(self): return [self] context = CompatibilityContext(context) signals.template_rendered.send(sender=self, template=self, context=context) return handler(context) class Jinja2(BaseEngine): app_dirname = "templates" @staticmethod @functools.lru_cache() def get_default(): """ When only one django-jinja backend is configured, returns it. Raises ImproperlyConfigured otherwise. This is required for finding the match extension where the developer does not specify a template_engine on a TemplateResponseMixin subclass. """ from django.template import engines jinja_engines = [engine for engine in engines.all() if isinstance(engine, Jinja2)] if len(jinja_engines) == 1: # Unwrap the Jinja2 engine instance. return jinja_engines[0] elif len(jinja_engines) == 0: raise ImproperlyConfigured( "No Jinja2 backend is configured.") else: raise ImproperlyConfigured( "Several Jinja2 backends are configured. " "You must select one explicitly.") def __init__(self, params): params = params.copy() options = params.pop("OPTIONS", {}).copy() self.app_dirname = options.pop("app_dirname", "templates") super(Jinja2, self).__init__(params) newstyle_gettext = options.pop("newstyle_gettext", True) context_processors = options.pop("context_processors", []) match_extension = options.pop("match_extension", ".jinja") match_regex = options.pop("match_regex", None) environment_clspath = options.pop("environment", "jinja2.Environment") extra_filters = options.pop("filters", {}) extra_tests = options.pop("tests", {}) extra_globals = options.pop("globals", {}) extra_constants = options.pop("constants", {}) translation_engine = options.pop("translation_engine", "django.utils.translation") tmpl_debug = options.pop("debug", settings.DEBUG) bytecode_cache = options.pop("bytecode_cache", {}) bytecode_cache.setdefault("name", "default") bytecode_cache.setdefault("enabled", False) bytecode_cache.setdefault("backend", "django_jinja.cache.BytecodeCache") undefined = options.pop("undefined", None) if undefined is not None: if isinstance(undefined, str): options["undefined"] = utils.load_class(undefined) else: options["undefined"] = undefined if settings.DEBUG: options.setdefault("undefined", jinja2.DebugUndefined) else: options.setdefault("undefined", jinja2.Undefined) environment_cls = import_string(environment_clspath) if isinstance(options.get("loader"), str): # Allow to specify a loader as string loader_cls = import_string(options.pop("loader")) else: # Backward compatible default loader_cls = jinja2.FileSystemLoader options.setdefault("loader", loader_cls(self.template_dirs)) options.setdefault("extensions", builtins.DEFAULT_EXTENSIONS) options.setdefault("auto_reload", settings.DEBUG) options.setdefault("autoescape", True) self.env = environment_cls(**options) # Initialize i18n support if settings.USE_I18N: translation = import_module(translation_engine) self.env.install_gettext_translations(translation, newstyle=newstyle_gettext) else: self.env.install_null_translations(newstyle=newstyle_gettext) self._context_processors = context_processors self._match_regex = match_regex self._match_extension = match_extension self._tmpl_debug = tmpl_debug self._bytecode_cache = bytecode_cache self._initialize_builtins(filters=extra_filters, tests=extra_tests, globals=extra_globals, constants=extra_constants) self._initialize_thirdparty() self._initialize_bytecode_cache() def _initialize_bytecode_cache(self): if self._bytecode_cache["enabled"]: cls = utils.load_class(self._bytecode_cache["backend"]) self.env.bytecode_cache = cls(self._bytecode_cache["name"]) def _initialize_thirdparty(self): """ Iterate over all available apps in searching and preloading available template filters or functions for jinja2. """ for app_path, mod_path in base._iter_templatetags_modules_list(): if not path.isdir(mod_path): continue for filename in filter(lambda x: x.endswith(".py") or x.endswith(".pyc"), os.listdir(mod_path)): # Exclude __init__.py files if filename == "__init__.py" or filename == "__init__.pyc": continue file_mod_path = "%s.templatetags.%s" % (app_path, filename.rsplit(".", 1)[0]) try: import_module(file_mod_path) except ImportError: pass library._update_env(self.env) def _initialize_builtins(self, filters=None, tests=None, globals=None, constants=None): def insert(data, name, value): if isinstance(value, str): data[name] = import_string(value) else: data[name] = value if filters: for name, value in filters.items(): insert(self.env.filters, name, value) if tests: for name, value in tests.items(): insert(self.env.tests, name, value) if globals: for name, value in globals.items(): insert(self.env.globals, name, value) if constants: for name, value in constants.items(): self.env.globals[name] = value @cached_property def context_processors(self): return tuple(import_string(path) for path in self._context_processors) @property def match_extension(self): return self._match_extension def from_string(self, template_code): return Template(self.env.from_string(template_code), self) def match_template(self, template_name): return base.match_template(template_name, self._match_extension, self._match_regex) def get_template(self, template_name): if not self.match_template(template_name): message = "Template {} does not exists".format(template_name) raise TemplateDoesNotExist(message) try: return Template(self.env.get_template(template_name), self) except jinja2.TemplateNotFound as exc: if utils.DJANGO_18: exc = TemplateDoesNotExist(exc.name) else: exc = TemplateDoesNotExist(exc.name, backend=self) utils.reraise( TemplateDoesNotExist, exc, sys.exc_info()[2], ) except jinja2.TemplateSyntaxError as exc: new = TemplateSyntaxError(exc.args) new.template_debug = get_exception_info(exc) utils.reraise(TemplateSyntaxError, new, sys.exc_info()[2]) @receiver(signals.setting_changed) def _setting_changed(sender, setting, *args, **kwargs): """ Reset the Jinja2.get_default() cached when TEMPLATES changes. """ if setting == "TEMPLATES": Jinja2.get_default.cache_clear() def get_exception_info(exception): """ Formats exception information for display on the debug page using the structure described in the template API documentation. """ context_lines = 10 lineno = exception.lineno if exception.source is None: if os.path.exists(exception.filename): with open(exception.filename, "r") as f: source = f.read() else: source = exception.source lines = list(enumerate(source.strip().split("\n"), start=1)) during = lines[lineno - 1][1] total = len(lines) top = max(0, lineno - context_lines - 1) bottom = min(total, lineno + context_lines) return { 'name': exception.filename, 'message': exception.message, 'source_lines': lines[top:bottom], 'line': lineno, 'before': '', 'during': during, 'after': '', 'total': total, 'top': top, 'bottom': bottom, } django-jinja-2.6.0/django_jinja/base.py000066400000000000000000000065331361517364100177710ustar00rootroot00000000000000import re import os import os.path as path from importlib import import_module import django from django.conf import settings from django.template.context import BaseContext def dict_from_context(context): """ Converts context to native python dict. """ if isinstance(context, BaseContext): new_dict = {} for i in reversed(list(context)): new_dict.update(dict_from_context(i)) return new_dict return dict(context) def _iter_templatetags_modules_list(): """ Get list of modules that contains templatetags submodule. """ from django.apps import apps all_modules = [x.name for x in apps.get_app_configs()] for app_path in all_modules: try: mod = import_module(app_path + ".templatetags") # Empty folders can lead to unexpected behavior with Python 3. # We make sure to have the `__file__` attribute. if getattr(mod, '__file__', None) is not None: yield (app_path, path.dirname(mod.__file__)) except ImportError: pass def patch_django_for_autoescape(): """ Patch django modules for make them compatible with jinja autoescape implementation. """ from django.utils import safestring from django.forms.forms import BoundField from django.forms.utils import ErrorList from django.forms.utils import ErrorDict if hasattr(safestring, "SafeText"): if not hasattr(safestring.SafeText, "__html__"): safestring.SafeText.__html__ = lambda self: str(self) if hasattr(safestring, "SafeString"): if not hasattr(safestring.SafeString, "__html__"): safestring.SafeString.__html__ = lambda self: str(self) if hasattr(safestring, "SafeUnicode"): if not hasattr(safestring.SafeUnicode, "__html__"): safestring.SafeUnicode.__html__ = lambda self: str(self) if hasattr(safestring, "SafeBytes"): if not hasattr(safestring.SafeBytes, "__html__"): safestring.SafeBytes.__html__ = lambda self: str(self) if not hasattr(BoundField, "__html__"): BoundField.__html__ = lambda self: str(self) if not hasattr(ErrorList, "__html__"): ErrorList.__html__ = lambda self: str(self) if not hasattr(ErrorDict, "__html__"): ErrorDict.__html__ = lambda self: str(self) def get_match_extension(using=None): """ Gets the extension that the template loader will match for django-jinja. This returns Jinja2.match_extension. The "using" parameter selects with Jinja2 backend to use if you have multiple ones configured in settings.TEMPLATES. If it is None and only one Jinja2 backend is defined then it will use that, otherwise an ImproperlyConfigured exception is thrown. """ from .backend import Jinja2 from django.template import engines if using is None: engine = Jinja2.get_default() else: engine = engines[using] return engine.match_extension def match_template(template_name, extension, regex): if extension: matches_extension = template_name.endswith(extension) if regex: return matches_extension and re.match(regex, template_name) else: return template_name.endswith(extension) elif regex: return re.match(regex, template_name) else: return True django-jinja-2.6.0/django_jinja/builtins/000077500000000000000000000000001361517364100203275ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/builtins/__init__.py000066400000000000000000000010511361517364100224350ustar00rootroot00000000000000DEFAULT_EXTENSIONS = [ "jinja2.ext.do", "jinja2.ext.loopcontrols", "jinja2.ext.with_", "jinja2.ext.i18n", "jinja2.ext.autoescape", "django_jinja.builtins.extensions.DebugExtension", "django_jinja.builtins.extensions.CsrfExtension", "django_jinja.builtins.extensions.CacheExtension", "django_jinja.builtins.extensions.TimezoneExtension", "django_jinja.builtins.extensions.UrlsExtension", "django_jinja.builtins.extensions.StaticFilesExtension", "django_jinja.builtins.extensions.DjangoFiltersExtension", ] django-jinja-2.6.0/django_jinja/builtins/extensions.py000066400000000000000000000256731361517364100231150ustar00rootroot00000000000000import logging import pprint import sys import django from django.conf import settings from django.contrib.staticfiles.storage import staticfiles_storage from django.core.cache import cache from jinja2.nodes import ContextReference try: from django.urls import NoReverseMatch from django.urls import reverse except ImportError: from django.core.urlresolvers import NoReverseMatch from django.core.urlresolvers import reverse from django.utils.formats import localize from django.utils.translation import pgettext from django.utils.translation import ugettext from jinja2 import Markup from jinja2 import TemplateSyntaxError from jinja2 import contextfunction from jinja2 import lexer from jinja2 import nodes from jinja2.ext import Extension try: from django.utils.encoding import force_str from django.utils.encoding import force_bytes except ImportError: from django.utils.encoding import force_unicode as force_str from django.utils.encoding import smart_str as force_bytes JINJA2_MUTE_URLRESOLVE_EXCEPTIONS = getattr(settings, "JINJA2_MUTE_URLRESOLVE_EXCEPTIONS", False) logger = logging.getLogger(__name__) # Compatibility with django <= 1.5 if django.VERSION[:2] <= (1, 5): import hashlib from django.utils.http import urlquote def make_template_fragment_key(fragm_name, vary_on): args_map = map(urlquote, vary_on) args_map = map(lambda x: force_bytes(x), args_map) args_string = b':'.join(args_map) args_hash = hashlib.md5(args_string).hexdigest() return 'template.cache.{0}.{1}'.format(fragm_name, args_hash) else: from django.core.cache.utils import make_template_fragment_key class CsrfExtension(Extension): tags = set(['csrf_token']) def __init__(self, environment): self.environment = environment def parse(self, parser): lineno = parser.stream.expect('name:csrf_token').lineno call = self.call_method( '_render', [nodes.Name('csrf_token', 'load', lineno=lineno)], lineno=lineno ) return nodes.Output([nodes.MarkSafe(call)]) def _render(self, csrf_token): if csrf_token: if csrf_token == 'NOTPROVIDED': return Markup("") return Markup("" % (csrf_token)) if settings.DEBUG: import warnings warnings.warn("A {% csrf_token %} was used in a template, but the context" "did not provide the value. This is usually caused by not " "using RequestContext.") return '' class CacheExtension(Extension): """ Exactly like Django's own tag, but supports full Jinja2 expressiveness for all arguments. {% cache gettimeout()*2 "foo"+options.cachename %} ... {% endcache %} General Syntax: {% cache [expire_time] [fragment_name] [var1] [var2] .. %} .. some expensive processing .. {% endcache %} Available by default (does not need to be loaded). Partly based on the ``FragmentCacheExtension`` from the Jinja2 docs. """ tags = set(['cache']) def parse(self, parser): lineno = next(parser.stream).lineno expire_time = parser.parse_expression() fragment_name = parser.parse_expression() vary_on = [] while not parser.stream.current.test('block_end'): vary_on.append(parser.parse_expression()) body = parser.parse_statements(['name:endcache'], drop_needle=True) return nodes.CallBlock( self.call_method('_cache_support', [expire_time, fragment_name, nodes.List(vary_on), nodes.Const(lineno)]), [], [], body).set_lineno(lineno) def _cache_support(self, expire_time, fragm_name, vary_on, lineno, caller): try: expire_time = int(expire_time) except (ValueError, TypeError): raise TemplateSyntaxError('"%s" tag got a non-integer timeout ' 'value: %r' % (list(self.tags)[0], expire_time), lineno) cache_key = make_template_fragment_key(fragm_name, vary_on) value = cache.get(cache_key) if value is None: value = caller() cache.set(cache_key, force_str(value), expire_time) else: value = force_str(value) return value class DebugExtension(Extension): """ A ``{% debug %}`` tag that dumps the available variables, filters and tests. Typical usage like this: .. codeblock:: html+jinja
{% debug %}
produces output like this: :: {'context': {'_': , 'csrf_token': , 'cycler': , ... 'view': }, 'filters': ['abs', 'add', 'addslashes', 'attr', 'batch', 'bootstrap', 'bootstrap_classes', 'bootstrap_horizontal', 'bootstrap_inline', ... 'yesno'], 'tests': ['callable', 'checkbox_field', 'defined', 'divisibleby', 'escaped', 'even', 'iterable', 'lower', 'mapping', 'multiple_checkbox_field', ... 'string', 'undefined', 'upper']} """ tags = set(['debug']) def __init__(self, environment): super(DebugExtension, self).__init__(environment) def parse(self, parser): lineno = parser.stream.expect('name:debug').lineno context = ContextReference() call = self.call_method('_render', [context], lineno=lineno) return nodes.Output([nodes.MarkSafe(call)]) def _render(self, context): result = { 'filters': sorted(self.environment.filters.keys()), 'tests': sorted(self.environment.tests.keys()), 'context': context.get_all() } # # We set the depth since the intent is basically to show the top few # names. TODO: provide user control over this? # if sys.version_info[:2] >= (3, 4): text = pprint.pformat(result, depth=3, compact=True) else: text = pprint.pformat(result, depth=3) text = Markup.escape(text) return text class StaticFilesExtension(Extension): def __init__(self, environment): super(StaticFilesExtension, self).__init__(environment) environment.globals["static"] = self._static def _static(self, path): return staticfiles_storage.url(path) class UrlsExtension(Extension): def __init__(self, environment): super(UrlsExtension, self).__init__(environment) environment.globals["url"] = self._url_reverse @contextfunction def _url_reverse(self, context, name, *args, **kwargs): try: current_app = context["request"].current_app except AttributeError: try: current_app = context["request"].resolver_match.namespace except AttributeError: current_app = None except KeyError: current_app = None try: return reverse(name, args=args, kwargs=kwargs, current_app=current_app) except NoReverseMatch as exc: logger.error('Error: %s', exc) if not JINJA2_MUTE_URLRESOLVE_EXCEPTIONS: raise return '' return reverse(name, args=args, kwargs=kwargs) from . import filters class TimezoneExtension(Extension): def __init__(self, environment): super(TimezoneExtension, self).__init__(environment) environment.globals["utc"] = filters.utc environment.globals["timezone"] = filters.timezone environment.globals["localtime"] = filters.localtime class DjangoFiltersExtension(Extension): def __init__(self, environment): super(DjangoFiltersExtension, self).__init__(environment) environment.filters["static"] = filters.static environment.filters["reverseurl"] = filters.reverse environment.filters["addslashes"] = filters.addslashes environment.filters["capfirst"] = filters.capfirst environment.filters["escapejs"] = filters.escapejs_filter environment.filters["floatformat"] = filters.floatformat environment.filters["iriencode"] = filters.iriencode environment.filters["linenumbers"] = filters.linenumbers environment.filters["make_list"] = filters.make_list environment.filters["slugify"] = filters.slugify environment.filters["stringformat"] = filters.stringformat environment.filters["truncatechars"] = filters.truncatechars environment.filters["truncatechars_html"] = filters.truncatechars_html environment.filters["truncatewords"] = filters.truncatewords environment.filters["truncatewords_html"] = filters.truncatewords_html environment.filters["urlizetrunc"] = filters.urlizetrunc environment.filters["ljust"] = filters.ljust environment.filters["rjust"] = filters.rjust environment.filters["cut"] = filters.cut environment.filters["linebreaksbr"] = filters.linebreaksbr environment.filters["linebreaks"] = filters.linebreaks_filter environment.filters["striptags"] = filters.striptags environment.filters["add"] = filters.add environment.filters["date"] = filters.date environment.filters["time"] = filters.time environment.filters["timesince"] = filters.timesince_filter environment.filters["timeuntil"] = filters.timeuntil_filter environment.filters["default_if_none"] = filters.default_if_none environment.filters["divisibleby"] = filters.divisibleby environment.filters["yesno"] = filters.yesno environment.filters["pluralize"] = filters.pluralize environment.filters["localtime"] = filters.localtime environment.filters["utc"] = filters.utc environment.filters["timezone"] = filters.timezone class DjangoExtraFiltersExtension(Extension): def __init__(self, environment): super(DjangoExtraFiltersExtension, self).__init__(environment) environment.filters["title"] = filters.title environment.filters["upper"] = filters.upper environment.filters["lower"] = filters.lower environment.filters["urlencode"] = filters.urlencode environment.filters["urlize"] = filters.urlize environment.filters["wordcount"] = filters.wordcount environment.filters["wordwrap"] = filters.wordwrap environment.filters["center"] = filters.center environment.filters["join"] = filters.join environment.filters["length"] = filters.length environment.filters["random"] = filters.random environment.filters["default"] = filters.default environment.filters["filesizeformat"] = filters.filesizeformat environment.filters["pprint"] = filters.pprint django-jinja-2.6.0/django_jinja/builtins/filters.py000066400000000000000000000067551361517364100223660ustar00rootroot00000000000000try: from django.utils.encoding import force_str except ImportError: from django.utils.encoding import force_unicode as force_str try: from django.urls import reverse as django_reverse except ImportError: from django.core.urlresolvers import reverse as django_reverse from django.contrib.staticfiles.storage import staticfiles_storage from jinja2 import Markup def reverse(value, *args, **kwargs): """ Shortcut filter for reverse url on templates. Is a alternative to django {% url %} tag, but more simple. Usage example: {{ 'web:timeline'|reverse(userid=2) }} This is a equivalent to django: {% url 'web:timeline' userid=2 %} """ return django_reverse(value, args=args, kwargs=kwargs) def static(path): return staticfiles_storage.url(path) from django.template.defaultfilters import addslashes from django.template.defaultfilters import capfirst from django.utils.html import escapejs as escapejs_filter # from django.utils.html import fix_ampersands as fix_ampersands_filter from django.template.defaultfilters import floatformat from django.template.defaultfilters import iriencode from django.template.defaultfilters import linenumbers from django.template.defaultfilters import make_list from django.template.defaultfilters import stringformat from django.template.defaultfilters import title from django.template.defaultfilters import truncatechars from django.template.defaultfilters import truncatechars_html from django.template.defaultfilters import truncatewords from django.template.defaultfilters import truncatewords_html from django.template.defaultfilters import upper from django.template.defaultfilters import lower from django.template.defaultfilters import urlencode from django.template.defaultfilters import urlize from django.template.defaultfilters import urlizetrunc from django.template.defaultfilters import wordcount from django.template.defaultfilters import wordwrap from django.template.defaultfilters import ljust from django.template.defaultfilters import rjust from django.template.defaultfilters import center from django.template.defaultfilters import cut from django.template.defaultfilters import linebreaks_filter from django.template.defaultfilters import linebreaksbr from django.template.defaultfilters import striptags from django.template.defaultfilters import join from django.template.defaultfilters import length from django.template.defaultfilters import random from django.template.defaultfilters import add from django.template.defaultfilters import date from django.template.defaultfilters import time from django.template.defaultfilters import timesince_filter from django.template.defaultfilters import timeuntil_filter from django.template.defaultfilters import default from django.template.defaultfilters import default_if_none from django.template.defaultfilters import divisibleby from django.template.defaultfilters import yesno from django.template.defaultfilters import filesizeformat from django.template.defaultfilters import pprint from django.template.defaultfilters import pluralize try: from django.utils.text import slugify as djslugify except ImportError: from django.template.defaultfilters import slugify as djslugify def slugify(value): return djslugify(force_str(value)) from functools import partial linebreaksbr = partial(linebreaksbr, autoescape=True) # TZ from django.templatetags.tz import do_timezone as timezone from django.templatetags.tz import localtime from django.templatetags.tz import utc django-jinja-2.6.0/django_jinja/cache.py000066400000000000000000000014161361517364100201150ustar00rootroot00000000000000import django from django.utils.functional import cached_property from jinja2 import BytecodeCache as _BytecodeCache class BytecodeCache(_BytecodeCache): """ A bytecode cache for Jinja2 that uses Django's caching framework. """ def __init__(self, cache_name): self._cache_name = cache_name @cached_property def backend(self): from django.core.cache import caches return caches[self._cache_name] def load_bytecode(self, bucket): key = 'jinja2_%s' % str(bucket.key) bytecode = self.backend.get(key) if bytecode: bucket.bytecode_from_string(bytecode) def dump_bytecode(self, bucket): key = 'jinja2_%s' % str(bucket.key) self.backend.set(key, bucket.bytecode_to_string()) django-jinja-2.6.0/django_jinja/contrib/000077500000000000000000000000001361517364100201365ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/__init__.py000066400000000000000000000000001361517364100222350ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/000077500000000000000000000000001361517364100234645ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/__init__.py000066400000000000000000000000321361517364100255700ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/models.py000066400000000000000000000000321361517364100253140ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/templatetags/000077500000000000000000000000001361517364100261565ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/templatetags/__init__.py000066400000000000000000000000321361517364100302620ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_easy_thumbnails/templatetags/thumbnails.py000066400000000000000000000022761361517364100307050ustar00rootroot00000000000000import logging from easy_thumbnails.conf import settings from easy_thumbnails.templatetags import thumbnail as _thumbnail from django_jinja import library from functools import wraps logger = logging.getLogger(__name__) def debug_silence(error_output=''): def inner(fn): @wraps(fn) def wrapper(*args, **kwargs): try: return fn(*args, **kwargs) except Exception as exc: if settings.THUMBNAIL_DEBUG: raise logger.error('Error: %s', exc) return error_output return wrapper return inner @library.filter @debug_silence(error_output='') def thumbnail_url(source, alias): return _thumbnail.thumbnail_url(source, alias) @library.global_function @debug_silence(error_output=None) def thumbnailer_passive(obj): return _thumbnail.thumbnailer_passive(obj) @library.global_function @debug_silence(error_output=None) def thumbnailer(obj): return _thumbnail.thumbnailer(obj) @library.global_function @debug_silence(error_output='') def thumbnail(source, **kwargs): thumbnail = _thumbnail.get_thumbnailer(source).get_thumbnail(kwargs) return thumbnail.url django-jinja-2.6.0/django_jinja/contrib/_humanize/000077500000000000000000000000001361517364100221155ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_humanize/__init__.py000066400000000000000000000000321361517364100242210ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_humanize/models.py000066400000000000000000000000321361517364100237450ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_humanize/templatetags/000077500000000000000000000000001361517364100246075ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_humanize/templatetags/__init__.py000066400000000000000000000000321361517364100267130ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_humanize/templatetags/_humanize.py000066400000000000000000000011271361517364100271410ustar00rootroot00000000000000from django.contrib.humanize.templatetags import humanize from django_jinja import library @library.filter def ordinal(source): return humanize.ordinal(source) @library.filter def intcomma(source, use_l10n=True): return humanize.intcomma(source, use_l10n) @library.filter def intword(source): return humanize.intword(source) @library.filter def apnumber(source): return humanize.apnumber(source) @library.filter def naturalday(source, arg=None): return humanize.naturalday(source, arg) @library.filter def naturaltime(source): return humanize.naturaltime(source) django-jinja-2.6.0/django_jinja/contrib/_pipeline/000077500000000000000000000000001361517364100221025ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_pipeline/__init__.py000066400000000000000000000000301361517364100242040ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_pipeline/models.py000066400000000000000000000000001361517364100237250ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_pipeline/templates/000077500000000000000000000000001361517364100241005ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_pipeline/templates/pipeline/000077500000000000000000000000001361517364100257055ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_pipeline/templates/pipeline/css.jinja000066400000000000000000000002351361517364100275120ustar00rootroot00000000000000 django-jinja-2.6.0/django_jinja/contrib/_pipeline/templates/pipeline/inline_js.jinja000066400000000000000000000002141361517364100306710ustar00rootroot00000000000000 django-jinja-2.6.0/django_jinja/contrib/_pipeline/templates/pipeline/js.jinja000066400000000000000000000002021361517364100273300ustar00rootroot00000000000000 django-jinja-2.6.0/django_jinja/contrib/_pipeline/templatetags/000077500000000000000000000000001361517364100245745ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_pipeline/templatetags/__init__.py000066400000000000000000000000321361517364100267000ustar00rootroot00000000000000# -*- coding: utf-8 -*- django-jinja-2.6.0/django_jinja/contrib/_pipeline/templatetags/_pipeline.py000066400000000000000000000053271361517364100271210ustar00rootroot00000000000000import jinja2 from django.contrib.staticfiles.storage import staticfiles_storage from django.template.loader import render_to_string from pipeline.conf import settings from pipeline.utils import guess_type from pipeline.packager import Packager, PackageNotFound from pipeline.collector import default_collector from django_jinja import library, utils @library.global_function @jinja2.contextfunction @utils.safe def compressed_css(ctx, name): package = settings.PIPELINE_CSS.get(name, {}) if package: package = {name: package} packager = Packager(css_packages=package, js_packages={}) try: package = packager.package_for('css', name) except PackageNotFound: return "" def _render_css(path): template_name = package.template_name or "pipeline/css.jinja" context = package.extra_context context.update({ 'type': guess_type(path, 'text/css'), 'url': staticfiles_storage.url(path) }) template = ctx.environment.get_template(template_name) return template.render(context) if settings.PIPELINE_ENABLED: return _render_css(package.output_filename) else: default_collector.collect() paths = packager.compile(package.paths) tags = [_render_css(path) for path in paths] return '\n'.join(tags) @library.global_function @jinja2.contextfunction @utils.safe def compressed_js(ctx, name): package = settings.PIPELINE_JS.get(name, {}) if package: package = {name: package} packager = Packager(css_packages={}, js_packages=package) try: package = packager.package_for("js", name) except PackageNotFound: return "" def _render_js(path): template_name = package.template_name or "pipeline/js.jinja" context = package.extra_context context.update({ 'type': guess_type(path, 'text/javascript'), 'url': staticfiles_storage.url(path), }) template = ctx.environment.get_template(template_name) return template.render(context) def _render_inline_js(js): context = package.extra_context context.update({ 'source': js }) template = ctx.environment.get_template("pipeline/inline_js.jinja") return template.render(context) # Render a optimized one if settings.PIPELINE_ENABLED: return _render_js(package.output_filename) else: default_collector.collect() paths = packager.compile(package.paths) templates = packager.pack_templates(package) tags = [_render_js(js) for js in paths] if templates: tags.append(_render_inline_js(templates)) return '\n'.join(tags) django-jinja-2.6.0/django_jinja/contrib/_subdomains/000077500000000000000000000000001361517364100224415ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_subdomains/__init__.py000066400000000000000000000000001361517364100245400ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_subdomains/models.py000066400000000000000000000000001361517364100242640ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_subdomains/templatetags/000077500000000000000000000000001361517364100251335ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_subdomains/templatetags/__init__.py000066400000000000000000000000001361517364100272320ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/contrib/_subdomains/templatetags/subdomainurls.py000066400000000000000000000004151361517364100303740ustar00rootroot00000000000000from django_jinja import library from jinja2 import contextfunction from subdomains.templatetags.subdomainurls import url as subdomain_url @library.global_function @contextfunction def url(context, *args, **kwargs): return subdomain_url(context, *args, **kwargs) django-jinja-2.6.0/django_jinja/library.py000066400000000000000000000043121361517364100205140ustar00rootroot00000000000000import functools import warnings from django.template.loader import render_to_string from django.utils.safestring import mark_safe # Global register dict for third party # template functions, filters and extensions. _local_env = { "globals": {}, "tests": {}, "filters": {}, "extensions": set([]), } def _update_env(env): """ Given a jinja environment, update it with third party collected environment extensions. """ env.globals.update(_local_env["globals"]) env.tests.update(_local_env["tests"]) env.filters.update(_local_env["filters"]) for extension in _local_env["extensions"]: env.add_extension(extension) def _attach_function(attr, func, name=None): if name is None: name = func.__name__ global _local_env _local_env[attr][name] = func return func def _register_function(attr, name=None, fn=None): if name is None and fn is None: def dec(func): return _attach_function(attr, func) return dec elif name is not None and fn is None: if callable(name): return _attach_function(attr, name) else: def dec(func): return _register_function(attr, name, func) return dec elif name is not None and fn is not None: return _attach_function(attr, fn, name) raise RuntimeError("Invalid parameters") def extension(extension): global _local_env _local_env["extensions"].add(extension) return extension def global_function(*args, **kwargs): return _register_function("globals", *args, **kwargs) def test(*args, **kwargs): return _register_function("tests", *args, **kwargs) def filter(*args, **kwargs): return _register_function("filters", *args, **kwargs) def render_with(template, fn=None): """ Makes simple function works like django's default inclusion_tag: render specified template with context returned by decorated function. """ if fn is None: return functools.partial(render_with, template) @functools.wraps(fn) def _wrapper(*args, **kwargs): data = render_to_string(template, fn(*args, **kwargs)) return mark_safe(data) return _wrapper django-jinja-2.6.0/django_jinja/loaders.py000066400000000000000000000025331361517364100205040ustar00rootroot00000000000000""" Api for django <= 1.7.x that uses loader extending way for get it working. """ import re import jinja2 from django.conf import settings from django.template import TemplateDoesNotExist from django.template.loaders import app_directories from django.template.loaders import filesystem from . import base if hasattr(settings, "DEFAULT_JINJA2_TEMPLATE_INTERCEPT_RE"): INTERCEPT_RE = getattr(settings, "DEFAULT_JINJA2_TEMPLATE_INTERCEPT_RE") REGEX = re.compile(INTERCEPT_RE) EXTENSION = None else: REGEX = None EXTENSION = getattr(settings, 'DEFAULT_JINJA2_TEMPLATE_EXTENSION', '.jinja') class LoaderMixin(object): is_usable = True def match_template(self, template_name): return base.match_template(template_name, regex=REGEX, extension=EXTENSION) def load_template(self, template_name, template_dirs=None): if self.match_template(template_name): try: template = base.env.get_template(template_name) return template, template.filename except jinja2.TemplateNotFound: raise TemplateDoesNotExist(template_name) else: return super(LoaderMixin, self).load_template(template_name, template_dirs) class FileSystemLoader(LoaderMixin, filesystem.Loader): pass class AppLoader(LoaderMixin, app_directories.Loader): pass django-jinja-2.6.0/django_jinja/management/000077500000000000000000000000001361517364100206125ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/management/__init__.py000066400000000000000000000000001361517364100227110ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/management/commands/000077500000000000000000000000001361517364100224135ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/management/commands/__init__.py000066400000000000000000000000001361517364100245120ustar00rootroot00000000000000django-jinja-2.6.0/django_jinja/management/commands/makemessages.py000066400000000000000000000062621361517364100254400ustar00rootroot00000000000000"""Jinja2's i18n functionality is not exactly the same as Django's. In particular, the tags names and their syntax are different: 1. The Django ``trans`` tag is replaced by a _() global. 2. The Django ``blocktrans`` tag is called ``trans``. (1) isn't an issue, since the whole ``makemessages`` process is based on converting the template tags to ``_()`` calls. However, (2) means that those Jinja2 ``trans`` tags will not be picked up my Django's ``makemessage`` command. There aren't any nice solutions here. While Jinja2's i18n extension does come with extraction capabilities built in, the code behind ``makemessages`` unfortunately isn't extensible, so we can: * Duplicate the command + code behind it. * Offer a separate command for Jinja2 extraction. * Try to get Django to offer hooks into makemessages(). * Monkey-patch. We are currently doing that last thing. It turns out there we are lucky for once: It's simply a matter of extending two regular expressions. Credit for the approach goes to: http://stackoverflow.com/questions/2090717/getting-translation-strings-for-jinja2-templates-integrated-with-django-1-x """ import re from django import VERSION as DJANGO_VERSION from django.core.management.commands import makemessages from django.template.base import BLOCK_TAG_START, BLOCK_TAG_END if DJANGO_VERSION[:2] < (1, 11): from django.utils.translation import trans_real else: from django.utils.translation import template as trans_real strip_whitespace_right = re.compile(r"(%s-?\s*(trans|pluralize).*?-%s)\s+" % (BLOCK_TAG_START, BLOCK_TAG_END), re.U) strip_whitespace_left = re.compile(r"\s+(%s-\s*(endtrans|pluralize).*?-?%s)" % (BLOCK_TAG_START, BLOCK_TAG_END), re.U) def strip_whitespaces(src): src = strip_whitespace_left.sub(r'\1', src) src = strip_whitespace_right.sub(r'\1', src) return src class Command(makemessages.Command): def handle(self, *args, **options): old_endblock_re = trans_real.endblock_re old_block_re = trans_real.block_re old_constant_re = trans_real.constant_re old_templatize = trans_real.templatize # Extend the regular expressions that are used to detect # translation blocks with an "OR jinja-syntax" clause. trans_real.endblock_re = re.compile( trans_real.endblock_re.pattern + '|' + r"""^-?\s*endtrans\s*-?$""") trans_real.block_re = re.compile( trans_real.block_re.pattern + '|' + r"""^-?\s*trans(?:\s+(?!'|")(?=.*?=.*?)|\s*-?$)""") trans_real.plural_re = re.compile( trans_real.plural_re.pattern + '|' + r"""^-?\s*pluralize(?:\s+.+|-?$)""") trans_real.constant_re = re.compile(r""".*?_\(((?:".*?(? django-jinja-2.6.0/doc/content.adoc000066400000000000000000000371151361517364100171370ustar00rootroot00000000000000= django-jinja - jinja2 backend for Django Andrey Antukh, 2.3.1 :toc: left :!numbered: :source-highlighter: pygments :pygments-style: friendly :sectlinks: == Introduction django-jinja is a xref:license[BSD Licensed], simple and non-obstructive jinja2 backend for Django. === Rationale Jinja2 provides certain advantages over the native system of Django, for example, explicit calls to callable from templates, has better performance and has a plugin system, etc ... Django comes with a jinja backend, why should I use *django-jinja*? The Django builtin backend has a very limited set of features if we compare it with the django template engine and in my opinion is not very usable because it does not integrate well with the rest of django such as its filters, template tags and preloading of templatetags, among others. *django-jinja* comes to the rescue and adds everything missing. This is a brief list of differences with django's built-in backend: - Auto-load templatetags compatible with Jinja2 the same way as Django. - Find the templates as usual in `"/templates"` directory instead of `"/jinja2"` directory (you can overwrite this behavior, see below). - Django templates can coexist with Jinja2 templates without any problems. It works as middleware, intercepts Jinja templates by file path pattern. - Django template filters and tags can mostly be used in Jinja2 templates. - I18n subsystem adapted for Jinja2 (makemessages now collects messages from Jinja templates) - Compatible with python2 and python3 using the same codebase. - jinja2 bytecode cache adapted for using django's cache subsystem. - Support for django context processors. [NOTE] ==== The usage of context processors is not the recommended way anymore, and with *django-jinja* you can done it setting global data or global constants. See below, in the django 1.8 configuration related section. ==== === Requirements - Python >= 3.5 - Django >= 1.11 - jinja2 >= 2.7.0 If you are using older versions of Django or Python, you need an older version of django-jinja: |=== |django-jinja |supported python versions |supported django versions |==1.4.2 |2.7, 3.3, 3.4 |1.5, 1.6, 1.7, 1.8 |==2.4.1 |2.7, 3.4, 3.5 |1.8, 1.11 |>=2.5.0 |3.5 (not django 3.0), 3.6, 3.7, 3.8 |1.11, 2.2, 3.0 |=== === Installation The simplest way to install **django-jinja** is using **pip**: [source, bash] ---- pip install django-jinja ---- == Quick Start Add it to Django's installed apps list: [source, python] ---- INSTALLED_APPS += ('django_jinja',) ---- Followed by the basic template engine configuration: [source, python] ---- TEMPLATES = [ { "BACKEND": "django_jinja.backend.Jinja2", "APP_DIRS": True, "OPTIONS": { "match_extension": ".jinja", } }, { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True }, ] ---- [NOTE] ==== If you are using the default value for the app templates directory of the django-jinja backend, take care of the template engines order, because the django-jinja backend by default uses the same directory for the templates as the django template engine. If you put the django engine first every jinja template will be found by the django engine. ==== == User Guide === Regex based template matching By default, *django-jinja* uses the file extension as the method to match templates, but if it is not enough, you can extend it using regular expressions: [source, python] ---- "OPTIONS": { "match_regex": r"^(?!admin/).*", # this is additive to match_extension } ---- To disable the extension matching, just set `"match_extension"` to `None`: [source, python] ---- "OPTIONS": { "match_extension": None, "match_regex": r"^(?!admin/).*", } ---- === Context processors support It is a helper to use django's context processors with jinja2 backend for django 1.8. .Example: set up a bunch of context processors: [source, python] ---- "OPTIONS": { "context_processors": [ "django.contrib.auth.context_processors.auth", "django.template.context_processors.debug", "django.template.context_processors.i18n", "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", ], } ---- As usual, this is a default list of context processors and you can skip setting them if you do not have your own. Furthermore, it is now not the recommended way to setup variables in the context and the purpose of its existence is a help for migrations. [NOTE] ==== Remember that django (1.8.x and 1.9.x) is backward compatible with the old template api and this has its own trade-offs. If you find yourself using functions like `render_to_string` or `render_to_response` from django, do not forget to pass the request parameter in order to make context processors work. ==== === Custom filters, globals, constants and tests This is a way to setup statically (in your settings) additional stuff for jinja: [source, python] ---- "OPTIONS": { "tests": { "mytest": "path.to.tests.mytestfn", }, "filters": { "myfilter": "path.to.filters.myfilterfn", }, "constants": { "hello": "hello world", }, "globals": { "somefn": "path.to.functions.somefn", } } ---- === Add additional extensions django-jinja, by default sets up a great amount of extensions to make your experience using jinja in django painless. But if you want to add more extensions, you can do it using the `extensions` entry of the backend options: [source, python] ---- from django_jinja.builtins import DEFAULT_EXTENSIONS "OPTIONS": { "extensions": DEFAULT_EXTENSIONS + [ # Your extensions here... "path.to.your.Extension" ] } ---- === Gettext Style Jinja2 implements two styles of gettext. You can read about it here: http://jinja.pocoo.org/docs/dev/extensions/#newstyle-gettext. You can switch to concrete style using the `newstyle_gettext` entry on backend options: [source, python] ---- "OPTIONS": { "newstyle_gettext": True, } ---- === Overwrite the default app templates directory As we said previously, django-jinja backend for django 1.8, uses the same directory for templates as the django template engine. But in some circumstances you may want to change it to use another directory. You can overwrite the default value with the `app_dirname` option: [source, python] ---- "OPTIONS": { "app_dirname": "jinja2", } ---- === Complete example This is a complete configuration example with django-jinja's defaults: [source, python] ---- TEMPLATES = [ { "BACKEND": "django_jinja.backend.Jinja2", "APP_DIRS": True, "OPTIONS": { # Match the template names ending in .html but not the ones in the admin folder. "match_extension": ".html", "match_regex": r"^(?!admin/).*", "app_dirname": "templates", # Can be set to "jinja2.Undefined" or any other subclass. "undefined": None, "newstyle_gettext": True, "tests": { "mytest": "path.to.my.test", }, "filters": { "myfilter": "path.to.my.filter", }, "globals": { "myglobal": "path.to.my.globalfunc", }, "constants": { "foo": "bar", }, "extensions": [ "jinja2.ext.do", "jinja2.ext.loopcontrols", "jinja2.ext.with_", "jinja2.ext.i18n", "jinja2.ext.autoescape", "django_jinja.builtins.extensions.CsrfExtension", "django_jinja.builtins.extensions.CacheExtension", "django_jinja.builtins.extensions.DebugExtension", "django_jinja.builtins.extensions.TimezoneExtension", "django_jinja.builtins.extensions.UrlsExtension", "django_jinja.builtins.extensions.StaticFilesExtension", "django_jinja.builtins.extensions.DjangoFiltersExtension", ], "bytecode_cache": { "name": "default", "backend": "django_jinja.cache.BytecodeCache", "enabled": False, }, "autoescape": True, "auto_reload": settings.DEBUG, "translation_engine": "django.utils.translation", } }, ] ---- == Differences with Django Template Engine === Url reversing django-jinja comes with helpers for reverse urls. Instead of using django's approach, it uses a simple function called `url`. .Reverse urls in templates [source, html+jinja] ---- {{ url('ns:name', pk=obj.pk) }} ---- This approach is very flexible, because we do not need additional options to set a result if executing url in one variable. With jinja2 you can use the set template tag for it: [source, html+jinja] ---- {% set myurl=url("ns:name", pk=obj.pk) %} ---- === Static files Like urls, static files can be resolved with the simple `static` function available globally in jinja context: .Example resolving static files [source, html+jinja] ---- {{ static("js/lib/foo.js") }} ---- === i18n support django-jinja inherits the jinja2 approach for handling translation strings. You can read more about it here: http://jinja.pocoo.org/docs/dev/templates/#i18n [source, html+jinja] ---- {{ _('Hello %(name)s', name=user.name) }} {% trans name=user.name %} Hello {{ name }} {% endtrans %} ---- Additionally, django-jinja extends django's `makemessages` command to make it work with jinja2 i18n tags. If you want more django-like i18n-related tags, you can use extensions from https://github.com/MoritzS/jinja2-django-tags. === Replace jinja filters with django versions Django and Jinja overlap in a little subset of template filters. To properly handle this, django-jinja uses the jinja versions by default. But if you want a django version of them, you should use the "django_jinja.builtins.extensions.DjangoExtraFiltersExtension" extension. The affected filters are: title, upper, lower, urlencode, urlize, wordcount, wordwrap, center join, length, random, default, filesizeformat, pprint. === Registering filters in a "django" way. django-jinja comes with facilities for loading template filters, globals and tests from django applications. Here an example: [source, python] ---- # /templatetags/.py # don't forget to create __init__.py in templatetags dir from django_jinja import library import jinja2 @library.test(name="one") def is_one(n): """ Usage: {% if m is one %}Foo{% endif %} """ return n == 1 @library.filter def mylower(name): """ Usage: {{ 'Hello'|mylower() }} """ return name.lower() @library.filter @jinja2.contextfilter def replace(context, value, x, y): """ Filter with template context. Usage: {{ 'Hello'|replace('H','M') }} """ return value.replace(x, y) @library.global_function def myecho(data): """ Usage: {{ myecho('foo') }} """ return data @library.global_function @library.render_with("test-render-with.jinja") def myrenderwith(*args, **kwargs): """ Render result with jinja template. Usage: {{ myrenderwith() }} """ return {"name": "Foo"} from .myextensions import MyExtension library.extension(MyExtension) ---- This only works within a Django app. If you don't have an app for your project, create an app specifically for this purpose and put your templatetags there. === Render 4xx/500 pages with jinja django-jinja also provides a set of views for easy render 4xx/500 pages using jinja engine: [source, python] ---- # yourproject/urls.py from django_jinja import views handler400 = views.BadRequest.as_view() handler403 = views.PermissionDenied.as_view() handler404 = views.PageNotFound.as_view() handler500 = views.ServerError.as_view() ---- == Known Issues - Previously to django 1.8, some way of using i18n related functions are not properly parsed with makemessages. == Builtin contrib modules *django-jinja* comes with some additional contrib modules that adapt a limited set of external django apps for easy use from jinja templates. Please note that in order to use any of these contrib modules, you'll need to install the relevant dependent packages yourself first. [NOTE] ==== In django, creating new tags is simpler than in Jinja2. You should remember that in jinja tags are really extensions and have a different purpose than the django template tags. Thus for many things that the django template system uses tags, django-jinja will provide functions with the same functionality. ==== django-pipeline ~~~~~~~~~~~~~~~ link:https://github.com/cyberdelia/django-pipeline[Pipeline] is an asset packaging library for Django (official description). [WARNING] ==== This plugin is deprecated, *django-pipeline* comes with good jinja support and it should be used. You can use the native *django-pipeline* suport for jinja using the "pipeline.jinja2.ext.PipelineExtension" extension. ==== .Activate plugin (settings.py) [source, python] ---- INSTALLED_APPS += ('django_jinja.contrib._pipeline',) ---- .Usage [source, html+jinja] ---- {{ compressed_css("alias") }} {{ compressed_js("alias") }} ---- easy-thumbnails ~~~~~~~~~~~~~~~ Easy Thumbnails is a thumbnail generation library for Django. .Activate plugin (settings.py) [source, python] ---- INSTALLED_APPS += ('django_jinja.contrib._easy_thumbnails',) ---- .Usage [source, html+jinja] ---- {{ thumbnail(file, size=(400, 400)) }} {{ user.avatar|thumbnail_url("alias") }} ---- django-subdomains ~~~~~~~~~~~~~~~~~ Subdomain helpers for the Django framework, including subdomain-based URL routing. .Activate plugin (settings.py) [source, python] ---- INSTALLED_APPS += ('django_jinja.contrib._subdomains',) ---- .Usage [source, html+jinja] ---- {{ url('homepage', subdomain='wildcard') }} ---- humanize ~~~~~~~~ Django comes with the humanize library that exposes some useful template filters. .Activate plugin (settings.py) [source, python] ---- INSTALLED_APPS += ('django_jinja.contrib._humanize',) ---- link:https://docs.djangoproject.com/en/dev/ref/contrib/humanize/[Complete list of available filters] .[[license]] License ------- [source,text] ---- Copyright (c) 2011-2017 Andre Antukh All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---- django-jinja-2.6.0/setup.py000066400000000000000000000035201361517364100155730ustar00rootroot00000000000000#!/usr/bin/env python3 from setuptools import setup import sys INSTALL_REQUIRES = [ "jinja2 >=2.10", "django >=1.11, <3.1", ] if sys.version_info < (2, 7): INSTALL_REQUIRES.append("importlib") setup( name = "django-jinja", version = "2.6.0", description = "Jinja2 templating language integrated in Django.", long_description = "", keywords = "django, jinja2", author = "Andrey Antukh", author_email = "auvipy@gmail.com", url = "https://github.com/niwinz/django-jinja", license = "BSD", packages = [ "django_jinja", "django_jinja.builtins", "django_jinja.management", "django_jinja.management.commands", "django_jinja.contrib", "django_jinja.contrib._pipeline", "django_jinja.contrib._pipeline.templatetags", "django_jinja.contrib._easy_thumbnails", "django_jinja.contrib._easy_thumbnails.templatetags", "django_jinja.contrib._humanize", "django_jinja.contrib._humanize.templatetags", "django_jinja.contrib._subdomains", "django_jinja.contrib._subdomains.templatetags", "django_jinja.views", "django_jinja.views.generic", ], install_requires = INSTALL_REQUIRES, tests_require = [ "pytz", ], classifiers = [ "Development Status :: 5 - Production/Stable", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Internet :: WWW/HTTP", ] ) django-jinja-2.6.0/testing/000077500000000000000000000000001361517364100155365ustar00rootroot00000000000000django-jinja-2.6.0/testing/locale/000077500000000000000000000000001361517364100167755ustar00rootroot00000000000000django-jinja-2.6.0/testing/locale/en/000077500000000000000000000000001361517364100173775ustar00rootroot00000000000000django-jinja-2.6.0/testing/locale/en/.hidden000066400000000000000000000000001361517364100206210ustar00rootroot00000000000000django-jinja-2.6.0/testing/manage.py000077500000000000000000000003621361517364100173440ustar00rootroot00000000000000#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-jinja-2.6.0/testing/runtests.py000066400000000000000000000005141361517364100177770ustar00rootroot00000000000000#!/usr/bin/env python import os, sys os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") if __name__ == "__main__": from django.core.management import execute_from_command_line args = sys.argv args.insert(1, "test") if len(args) == 2: args.insert(2, "testapp") execute_from_command_line(args) django-jinja-2.6.0/testing/settings.py000066400000000000000000000106421361517364100177530ustar00rootroot00000000000000import os, sys import django sys.path.insert(0, "..") BASE_DIR = os.path.dirname(__file__) DEBUG = False TEMPLATE_DEBUG = False ALLOWED_HOSTS = ["*"] DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": "foobar.db" } } MIDDLEWARE_CLASSES = [ # "django.middleware.csrf.CsrfViewMiddleware", "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ] ROOT_URLCONF = "testapp.urls" USE_I18N = True USE_TZ = True TIME_ZONE = "UTC" LANGUAGE_CODE = "en" ADMIN_MEDIA_PREFIX = "/static/admin/" INTERNAL_IPS = ("127.0.0.1",) #STATICFILES_STORAGE = "pipeline.storage.PipelineStorage" STATIC_ROOT = os.path.join(BASE_DIR, "static") STATIC_URL = "/static/" PIPELINE_CSS_COMPRESSOR = None PIPELINE_JS_COMPRESSOR = None PIPELINE_ENABLE = False SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" SESSION_EXPIRE_AT_BROWSER_CLOSE = True STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", "pipeline.finders.PipelineFinder", "pipeline.finders.CachedFileFinder", ) # TEMPLATE_DIRS = () SECRET_KEY = "di!n($kqa3)nd%ikad#kcjpkd^uw*h%*kj=*pm7$vbo6ir7h=l" INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.staticfiles", "django.contrib.messages", "pipeline", "django_jinja", "django_jinja.contrib._pipeline", "testapp", ) from django_jinja.builtins import DEFAULT_EXTENSIONS JINJA2_MUTE_URLRESOLVE_EXCEPTIONS = True if django.VERSION[:2] <= (1, 7): TEMPLATE_LOADERS = [ "django_jinja.loaders.AppLoader", "django_jinja.loaders.FileSystemLoader" ] JINJA2_CONSTANTS = {"foo": "bar"} JINJA2_AUTOESCAPE = True DEFAULT_JINJA2_TEMPLATE_EXTENSION = ".jinja" JINJA2_EXTENSIONS = DEFAULT_EXTENSIONS + [ "django_jinja.builtins.extensions.DjangoExtraFiltersExtension", ] else: TEMPLATES = [ { "BACKEND": "django_jinja.backend.Jinja2", "NAME": "jinja2", "APP_DIRS": True, "OPTIONS": { "debug": True, "context_processors": [ "django.contrib.auth.context_processors.auth", "django.template.context_processors.debug", "django.template.context_processors.i18n", "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", ], "constants": { "foo": "bar", }, "extensions": DEFAULT_EXTENSIONS + [ "django_jinja.builtins.extensions.DjangoExtraFiltersExtension", ] } }, { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True } ] PIPELINE_CSS = { "test": { "source_filenames": ["style.css"], "output_filename": "style.2.css", "extra_context": {"media": "all"}, }, "test2": { "source_filenames": ["style.css"], "output_filename": "style.2.css", } } PIPELINE_JS = { "test": { "source_filenames": ["script.js"], "output_filename": "script.2.js", } } import django if django.VERSION[:2] >= (1, 6): TEST_RUNNER = "django.test.runner.DiscoverRunner" LOGGING = { "version": 1, "disable_existing_loggers": False, "filters": { "require_debug_false": { "()": "django.utils.log.RequireDebugFalse" } }, "formatters": { "simple": { "format": "%(asctime)s: %(message)s", } }, "handlers": { "console":{ "level":"DEBUG", "class":"logging.StreamHandler", "formatter": "simple" }, }, "loggers": { "django.request": { "handlers": ["console"], "level": "ERROR", "propagate": True, }, "arandomtable.custom": { "handlers": ["console"], "level": "DEBUG", }, } } django-jinja-2.6.0/testing/testapp/000077500000000000000000000000001361517364100172165ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/__init__.py000066400000000000000000000000001361517364100213150ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/forms.py000066400000000000000000000001371361517364100207170ustar00rootroot00000000000000from django import forms class TestForm(forms.Form): name = forms.CharField(max_length=2) django-jinja-2.6.0/testing/testapp/jinja2/000077500000000000000000000000001361517364100203735ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/jinja2/hola_mundo.html000066400000000000000000000000311361517364100234000ustar00rootroot00000000000000hola mundo de {{ name }} django-jinja-2.6.0/testing/testapp/migrations/000077500000000000000000000000001361517364100213725ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/migrations/0001_initial.py000066400000000000000000000006601361517364100240370ustar00rootroot00000000000000from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='TestModel', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={ }, bases=(models.Model,), ), ] django-jinja-2.6.0/testing/testapp/migrations/0002_testmodel_date.py000066400000000000000000000005501361517364100254020ustar00rootroot00000000000000from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ('testapp', '0001_initial'), ] operations = [ migrations.AddField( model_name='testmodel', name='date', field=models.DateTimeField(null=True), preserve_default=True, ), ] django-jinja-2.6.0/testing/testapp/migrations/__init__.py000066400000000000000000000000001361517364100234710ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/models.py000066400000000000000000000001601361517364100210500ustar00rootroot00000000000000from django.db.models import Model, DateTimeField class TestModel(Model): date = DateTimeField(null=True) django-jinja-2.6.0/testing/testapp/static/000077500000000000000000000000001361517364100205055ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/static/script.js000066400000000000000000000000341361517364100223440ustar00rootroot00000000000000console.log("Hello world"); django-jinja-2.6.0/testing/testapp/static/style.css000066400000000000000000000000321361517364100223520ustar00rootroot00000000000000body { font-size: 12px; } django-jinja-2.6.0/testing/testapp/templates/000077500000000000000000000000001361517364100212145ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/templates/403.jinja000066400000000000000000000000041361517364100225310ustar00rootroot00000000000000403 django-jinja-2.6.0/testing/testapp/templates/404.jinja000066400000000000000000000000041361517364100225320ustar00rootroot00000000000000404 django-jinja-2.6.0/testing/testapp/templates/500.jinja000066400000000000000000000000041361517364100225270ustar00rootroot00000000000000500 django-jinja-2.6.0/testing/testapp/templates/hello_world.jinja000066400000000000000000000002651361517364100245460ustar00rootroot00000000000000 test

Hello World from {{ name }}

{% csrf_token %} django-jinja-2.6.0/testing/testapp/templates/i18n_test.jinja000066400000000000000000000011141361517364100240440ustar00rootroot00000000000000{% trans bar=foo %} Foo {{ bar }} {% endtrans %} {{ _('Index') }} {{ table_sort('date_start', _('Year')) }}

{% trans %}1 Hello {{ user }}!{% endtrans %}

{% trans user=user.username %}2 Hello {{ user }}!{% endtrans %}

{% trans book_title=book.title, author=author.name %} 3 This is {{ book_title }} by {{ author }} {% endtrans %} {% trans count=list|length %} 4 There is {{ count }} {{ name }} object. {% pluralize %} 4 There are {{ count }} {{ name }} objects. {% endtrans %} {{ _('5 Hello World!') }} {{ _('6 Hello %(user)s!')|format(user=user.username) }} django-jinja-2.6.0/testing/testapp/templates/pipeline_test.jinja000066400000000000000000000003711361517364100250760ustar00rootroot00000000000000 Pipeline test page

Hello world with pipeline and jinja2

{{ compressed_js("test") }} {# {% javascript "test" %} #} django-jinja-2.6.0/testing/testapp/templates/streaming_test.jinja000066400000000000000000000002651361517364100252640ustar00rootroot00000000000000 Streaming test

Streaming to the World from {{ name }}

django-jinja-2.6.0/testing/testapp/templates/test-debug-var.jinja000066400000000000000000000000411361517364100250550ustar00rootroot00000000000000{% if debug %}foobar{% endif -%} django-jinja-2.6.0/testing/testapp/templates/test-render-with.jinja000066400000000000000000000000341361517364100254330ustar00rootroot00000000000000{{ name }} django-jinja-2.6.0/testing/testapp/templates/testapp/000077500000000000000000000000001361517364100226745ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive.html.jinja000066400000000000000000000000371361517364100303550ustar00rootroot00000000000000ArchiveIndexView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive_day.html.jinja000066400000000000000000000000351361517364100312100ustar00rootroot00000000000000DayArchiveView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive_month.html.jinja000066400000000000000000000000371361517364100315620ustar00rootroot00000000000000MonthArchiveView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive_today.html.jinja000066400000000000000000000000371361517364100315550ustar00rootroot00000000000000TodayArchiveView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive_week.html.jinja000066400000000000000000000000361361517364100313670ustar00rootroot00000000000000WeekArchiveView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_archive_year.html.jinja000066400000000000000000000000361361517364100313740ustar00rootroot00000000000000YearArchiveView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_confirm_delete.html.jinja000066400000000000000000000000311361517364100317050ustar00rootroot00000000000000DeleteView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_create.html.jinja000066400000000000000000000000311361517364100301710ustar00rootroot00000000000000CreateView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_date_detail.html.jinja000066400000000000000000000000351361517364100311710ustar00rootroot00000000000000DateDetailView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_detail.html.jinja000066400000000000000000000000311361517364100301700ustar00rootroot00000000000000DetailView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_list.html.jinja000066400000000000000000000000271361517364100277060ustar00rootroot00000000000000ListView Test Template django-jinja-2.6.0/testing/testapp/templates/testapp/testmodel_update.html.jinja000066400000000000000000000000311361517364100302100ustar00rootroot00000000000000UpdateView Test Template django-jinja-2.6.0/testing/testapp/templatetags/000077500000000000000000000000001361517364100217105ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/templatetags/__init__.py000066400000000000000000000000001361517364100240070ustar00rootroot00000000000000django-jinja-2.6.0/testing/testapp/templatetags/sample_addons.py000066400000000000000000000006331361517364100250750ustar00rootroot00000000000000from django_jinja import library import jinja2 @library.test(name="one") def is_one(n): return n == 1 @library.filter @jinja2.contextfilter def replace(context, value, x, y): return value.replace(x, y) @library.global_function def myecho(data): return data @library.global_function @library.render_with("test-render-with.jinja") def myrenderwith(*args, **kwargs): return {"name": "Foo"} django-jinja-2.6.0/testing/testapp/tests.py000066400000000000000000000516411361517364100207410ustar00rootroot00000000000000import datetime try: import unittest.mock as mock except ImportError: import mock import django from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch from django.urls import reverse from django.middleware import csrf from django.shortcuts import render from django.template import RequestContext from django.template import engines from django.template.loader import get_template from django.test import TestCase from django.test import override_settings from django.test.client import RequestFactory from django_jinja.base import get_match_extension from django_jinja.base import match_template from django_jinja.views.generic.base import Jinja2TemplateResponseMixin from .forms import TestForm from .models import TestModel from .views import StreamingTestView class RenderTemplatesTests(TestCase): def setUp(self): self.env = engines["jinja2"] self.factory = RequestFactory() def test_template_filters(self): filters_data = [ ("{{ 'test-static.css'|static }}", {}, '/static/test-static.css'), ("{{ 'test-1'|reverseurl }}", {}, '/test1/'), ("{{ 'test-1'|reverseurl(data=2) }}", {}, '/test1/2/'), ("{{ num|floatformat }}", {'num': 34.23234}, '34.2'), ("{{ num|floatformat(3) }}", {'num': 34.23234}, '34.232'), ("{{ 'hola'|capfirst }}", {}, "Hola"), # The list of Django 1.11 truncator / Django 2.2 truncator result. ("{{ 'hola mundo'|truncatechars(5) }}", {}, ["ho...", "hola…"]), ("{{ 'hola mundo'|truncatechars_html(5) }}", {}, ["ho...", "hola…"]), ("{{ 'hola mundo'|truncatewords(1) }}", {}, ["hola ...", "hola …"]), ("{{ 'hola mundo'|truncatewords_html(1) }}", {}, ["hola ...", "hola …"]), ("{{ 'hola mundo'|wordwrap(1) }}", {}, "hola\nmundo"), ("{{ 'hola mundo'|title }}", {}, "Hola Mundo"), ("{{ 'hola mundo'|slugify }}", {}, "hola-mundo"), ("{{ 'hello'|ljust(10) }}", {}, "hello "), ("{{ 'hello'|rjust(10) }}", {}, " hello"), # Django 2.2 does not close br tag. ("{{ 'hello\nworld'|linebreaksbr }}", {}, ["hello
world", "hello
world"]), ("{{ '
hello
'|striptags }}", {}, "hello"), ("{{ list|join(',') }}", {'list':['a','b']}, 'a,b'), ("{{ 3|add(2) }}", {}, "5"), ("{{ now|date('n Y') }}", {"now": datetime.datetime(2012, 12, 20)}, "12 2012"), ("{{ url('test-1') }}", {}, '/test1/'), ("{{ foo }}", {}, "bar"), ] print() for template_str, kwargs, result in filters_data: print("- Testing: ", template_str, "with:", kwargs) template = self.env.from_string(template_str) _result = template.render(kwargs) if isinstance(result, str): self.assertEqual(_result, result) else: self.assertTrue(_result in result) def test_string_interpolation(self): template = self.env.from_string("{{ 'Hello %s!' % name }}") self.assertEqual(template.render({"name": "foo"}), "Hello foo!") template = self.env.from_string("{{ _('Hello %s!').format(name) }}") self.assertEqual(template.render({"name": "foo"}), "Hello foo!") def test_urlresolve_exceptions(self): template = self.env.from_string("{{ url('adads') }}") template.render({}) def test_custom_addons_01(self): template = self.env.from_string("{{ 'Hello'|replace('H','M') }}") result = template.render({}) self.assertEqual(result, "Mello") def test_custom_addons_02(self): template = self.env.from_string("{% if m is one %}Foo{% endif %}") result = template.render({'m': 1}) self.assertEqual(result, "Foo") def test_custom_addons_03(self): template = self.env.from_string("{{ myecho('foo') }}") result = template.render({}) self.assertEqual(result, "foo") def test_render_with(self): template = self.env.from_string("{{ myrenderwith() }}") result = template.render({}) self.assertEqual(result, "Foo") def test_django_context(self): """ Test that Django context objects (which are stacks of dicts) can be passed directly to Jinja2 templates. """ template = self.env.from_string("{{ greeting }}, {{ name }}") request = self.factory.get('/') ctx = RequestContext(request, {"greeting": "Hello", "name": "stranger"}) with ctx.push(greeting="Hi"): with ctx.push(name="friend"): rendered1 = template.render(ctx) rendered2 = template.render(ctx) self.assertEqual(rendered1, "Hi, friend") self.assertEqual(rendered2, "Hello, stranger") def test_autoscape_with_form(self): form = TestForm() template = self.env.from_string("{{ form.as_p() }}") result = template.render({"form": form}) self.assertIn('maxlength="2"', result) # Django 2.2 does not use "/>" for input html. self.assertIn("" for input html. self.assertIn("
  • Ensure this value """ """has at most 2 characters (it has 3).
  • """)) template = self.env.from_string("{{ form.errors }}") result = template.render({"form": form}) self.assertEqual(result, ("""
    • name
        """ """
      • Ensure this value has at most 2 characters (it """ """has 3).
    """)) def test_autoescape_01(self): template = self.env.from_string("{{ foo|safe }}") result = template.render({'foo': '

    Hellp

    '}) self.assertEqual(result, "

    Hellp

    ") def test_autoescape_02(self): template = self.env.from_string("{{ foo }}") result = template.render({'foo': '

    Hellp

    '}) self.assertEqual(result, "<h1>Hellp</h1>") def test_autoescape_03(self): template = self.env.from_string("{{ foo|linebreaksbr }}") result = template.render({"foo": "\nfoo"}) self.assertTrue(result in [ "<script>alert(1)</script>
    foo", # Django 1.11 "<script>alert(1)</script>
    foo", # Django 2.2 ]) def test_debug_var_when_render_shortcut_is_used(self): prev_debug_value = settings.DEBUG settings.DEBUG = True request = self.factory.get("/") response = render(request, "test-debug-var.jinja") self.assertEqual(response.content, b"foobar") settings.DEBUG = prev_debug_value def test_debug_tag(self): """Test for {% debug %}""" tmpl = self.env.from_string('''Hello{% debug %}Bye''') out = tmpl.render() out = out.replace(''', "'").replace('<', '<').replace('>', '>') # # Check that some of the built-in items exist in the debug output... # assert "'context'" in out assert "'cycler'" in out assert "'filters'" in out assert "'abs'" in out assert "'tests'" in out assert "'!='" in out def test_csrf_01(self): template_content = "{% csrf_token %}" with mock.patch("django.middleware.csrf.get_token", return_value="123123") as m: request = self.factory.get('/customer/details') token = csrf.get_token(request) if django.VERSION[:2] >= (1, 8): template = self.env.from_string(template_content) result = template.render({}, request) else: context = RequestContext(request) template = self.env.from_string(template_content) result = template.render(context) expected = ("").format(token) self.assertEqual(token, "123123") self.assertEqual(result, expected) def test_cache_01(self): template_content = "{% cache 200 'fooo' %}fóäo bar{% endcache %}" request = self.factory.get('/customer/details') context = RequestContext(request) template = self.env.from_string(template_content) result = template.render(context) self.assertEqual(result, "fóäo bar") def test_404_page(self): response = self.client.get(reverse("page-404")) self.assertEqual(response.status_code, 404) self.assertEqual(response.content, b"404") response = self.client.post(reverse("page-404")) self.assertEqual(response.status_code, 404) self.assertEqual(response.content, b"404") response = self.client.put(reverse("page-404")) self.assertEqual(response.status_code, 404) self.assertEqual(response.content, b"404") response = self.client.delete(reverse("page-404")) self.assertEqual(response.status_code, 404) self.assertEqual(response.content, b"404") def test_403_page(self): response = self.client.get(reverse("page-403")) self.assertEqual(response.status_code, 403) self.assertEqual(response.content, b"403") def test_500_page(self): response = self.client.get(reverse("page-500")) self.assertEqual(response.status_code, 500) self.assertEqual(response.content, b"500") def test_get_default(self): from django_jinja.backend import Jinja2 Jinja2.get_default.cache_clear() self.assertEqual(Jinja2.get_default(), self.env) def test_get_default_multiple(self): from django_jinja.backend import Jinja2 setting = { "append": [ { "BACKEND": "django_jinja.backend.Jinja2", "NAME": "jinja2dup", "APP_DIRS": True, "OPTIONS": { "match_extension": ".jinjadup", } } ] } with self.modify_settings(TEMPLATES=setting): with self.assertRaisesRegexp(ImproperlyConfigured, r'Several Jinja2 backends'): Jinja2.get_default() def test_get_default_none(self): from django.conf import global_settings from django_jinja.backend import Jinja2 with self.settings(TEMPLATES=global_settings.TEMPLATES): with self.assertRaisesRegexp(ImproperlyConfigured, r'No Jinja2 backend is configured'): Jinja2.get_default() def test_overwrite_default_app_dirname(self): setting = [ { "BACKEND": "django_jinja.backend.Jinja2", "NAME": "jinja2", "APP_DIRS": True, "OPTIONS": { "match_extension": None, "match_regex": None, "app_dirname": "jinja2", } } ] with override_settings(TEMPLATES=setting): template = get_template("hola_mundo.html") data = template.render({"name": "jinja2"}) self.assertEqual(data, "hola mundo de jinja2") def test_context_manipulation(self): response = self.client.get(reverse("test-1")) self.assertEqual(response.context["name"], "Jinja2") self.assertTemplateUsed(response, 'hello_world.jinja') def test_streaming_response(self): template = "streaming_test.jinja" context = {"view": StreamingTestView, "name": "Streaming Jinja2"} response = self.client.get(reverse('streaming-test')) self.assertEqual(response.context["name"], context["name"]) self.assertEqual(response.context["view"], context["view"]) self.assertTemplateUsed(response, template) template = get_template(template) self.assertEqual( b''.join(response.streaming_content), template.render(context).encode() ) class DjangoPipelineTestTest(TestCase): def setUp(self): self.env = engines["jinja2"] def test_pipeline_js_safe(self): template = self.env.from_string("{{ compressed_js('test') }}") result = template.render({}) self.assertTrue(result.startswith("\d+)/$", BasicTestView.as_view(), name="test-1"), url(r"^test-pipeline/$", PipelineTestView.as_view(), name="pipeline-test"), url(r"^test/404$", views.PageNotFound.as_view(), name="page-404"), url(r"^test/403$", views.PermissionDenied.as_view(), name="page-403"), url(r"^test/500$", views.ServerError.as_view(), name="page-500"), url(r"^test-streaming/$", StreamingTestView.as_view(), name='streaming-test'), url(r"^testmodel/$", ListTestView.as_view()), url(r"^testmodel/create$", CreateTestView.as_view()), url(r"^testmodel/(?P\d+)/delete$", DeleteTestView.as_view()), url(r"^testmodel/(?P\d+)/detail$", DetailTestView.as_view()), url(r"^testmodel/(?P\d+)/update$", UpdateTestView.as_view()), url(r"^testmodel/archive/$", ArchiveIndexTestView.as_view()), url(r"^testmodel/archive/(?P\d{4})/$", YearArchiveTestView.as_view()), url(r"^testmodel/archive/(?P\d{4})/week/(?P\d+)/$", WeekArchiveTestView.as_view()), url(r"^testmodel/archive/(?P\d{4})/(?P[\w-]+)/$", MonthArchiveTestView.as_view()), url(r"^testmodel/archive/(?P\d{4})/(?P[\w-]+)/(?P\d+)/$", DayArchiveTestView.as_view()), url(r"^testmodel/archive/today/$", TodayArchiveTestView.as_view()), url(r"^testmodel/archive/(?P\d{4})/(?P[\w-]+)/(?P\d+)/(?P\d+)$", DateDetailTestView.as_view()) ] django-jinja-2.6.0/testing/testapp/views.py000066400000000000000000000050711361517364100207300ustar00rootroot00000000000000from django.views.generic import View from django.http import HttpResponse, StreamingHttpResponse from django.shortcuts import render from django.template import loader from django.template.loader import render_to_string from django_jinja.views.generic.detail import DetailView from django_jinja.views.generic.edit import CreateView, DeleteView, UpdateView from django_jinja.views.generic.list import ListView from django_jinja.views.generic.dates import ArchiveIndexView, YearArchiveView, MonthArchiveView, WeekArchiveView, DayArchiveView, TodayArchiveView, DateDetailView from .models import TestModel class BasicTestView(View): def get(self, request, data=None): data = render_to_string("hello_world.jinja", {"name": "Jinja2"}, request=request) return HttpResponse(data) class PipelineTestView(View): def get(self, request, data=None): return render(request, "pipeline_test.jinja") class ContextManipulationTestView(View): def get(self, request): return render(request, "hello_world.jinja", {"name": "Jinja2"}) # ==== generic.detail ==== class DetailTestView(DetailView): model = TestModel # ==== generic.edit ==== class CreateTestView(CreateView): model = TestModel fields = [] template_name_suffix = '_create' class DeleteTestView(DeleteView): model = TestModel class UpdateTestView(UpdateView): model = TestModel fields = [] template_name_suffix = '_update' # ==== generic.list ==== class ListTestView(ListView): model = TestModel # ==== generic.dates ==== class ArchiveIndexTestView(ArchiveIndexView): model = TestModel date_field = 'date' class YearArchiveTestView(YearArchiveView): model = TestModel date_field = 'date' class MonthArchiveTestView(MonthArchiveView): model = TestModel date_field = 'date' class WeekArchiveTestView(WeekArchiveView): model = TestModel date_field = 'date' class DayArchiveTestView(DayArchiveView): model = TestModel date_field = 'date' class TodayArchiveTestView(TodayArchiveView): model = TestModel date_field = 'date' template_name_suffix = '_archive_today' class DateDetailTestView(DateDetailView): model = TestModel date_field = 'date' template_name_suffix = '_date_detail' class StreamingTestView(View): def get(self, request, *args, **kwargs): context = {"name": "Streaming Jinja2", "view": type(self)} template = loader.get_template('streaming_test.jinja') return StreamingHttpResponse(template.stream(context, request), content_type='text/html') django-jinja-2.6.0/testing/uwsgi.ini000066400000000000000000000003131361517364100173720ustar00rootroot00000000000000[uwsgi] http-socket = 127.0.0.1:8001 ;pyhome = /home/niwi/.virtualenvs/djorm enable-threads = true wsgi-file = wsgi.py processes = 2 threads = 2 master = true max-requests = 2 ;lazy = true ;cheap = true django-jinja-2.6.0/testing/wsgi.py000066400000000000000000000021521361517364100170610ustar00rootroot00000000000000""" WSGI config for arandomtable project. This module contains the WSGI application used by Django's development server and any production WSGI deployments. It should expose a module-level variable named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover this application via the ``WSGI_APPLICATION`` setting. Usually you will have the standard Django WSGI application here, but it also might make sense to replace the whole Django WSGI application with a custom one that later delegates to the Django one. For example, you could introduce WSGI middleware here, or combine a Django application with an application of another framework. """ import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") # This application object is used by any WSGI server configured to use this # file. This includes Django's development server, if the WSGI_APPLICATION # setting points here. from django.core.wsgi import get_wsgi_application application = get_wsgi_application() # Apply WSGI middleware here. # from helloworld.wsgi import HelloWorldApplication # application = HelloWorldApplication(application) django-jinja-2.6.0/tox.ini000066400000000000000000000004621361517364100153760ustar00rootroot00000000000000[tox] envlist = py{35,36}-django111 py{35,36,37,38}-django22 py{36,37,38}-django30 [testenv] changedir=testing commands=python runtests.py deps= django111: Django>=1.11,<2.0 django22: Django>=2.2,<3.0 django30: Django>=3.0,<3.1 jinja2 django-pipeline<1.6 pytz mock