Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Make templates more reusable by Improving template loading algorithm to avoid extending infinite recursion #217

Closed
wants to merge 8 commits into from

5 participants

@akaariai

Please remove...

This was removed in this commit (2 minutes after) bf23921

@yedpodtrzitko

What is this if-else for, when it contains the same code?

Probably LoaderOriginLite should be in the second line

This a intermediate step. Please, see the final diff:

https://github.com/django/django/pull/217/files#L2R81

@goinnn

didn't I convince you? :-( Tell me something

@apollo13
Owner

@goinnn as @akaariai noted on the ticket this patch needs tests and docs! (And yes I'd really like to see this feature in core :))

@goinnn

Hi @apollo13. I'm sorry but I was trying for two years to add this feature. I'm bored to try it. If you can get it. Congratulantions!

@apollo13
Owner

Ok, closing for now then. If someone else wants to work on it please open a new pull request for review.

@apollo13 apollo13 closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
6 django/template/base.py
@@ -227,9 +227,14 @@ def create_token(self, token_string, in_tag):
else:
token = Token(TOKEN_TEXT, token_string)
token.lineno = self.lineno
+ # token.source is a tuple, this contains origin object and
+ # the range of the columns where is this token.
+ # If TEMPLATE_DEBUG = False we don't need it, therefore we set with (-1, -1)
+ token.source = self.origin, (-1, -1)
self.lineno += token_string.count('\n')
return token
+
class Parser(object):
def __init__(self, tokens):
self.tokens = tokens
@@ -304,6 +309,7 @@ def extend_nodelist(self, nodelist, node, token):
"in the template." % node)
if isinstance(nodelist, NodeList) and not isinstance(node, TextNode):
nodelist.contains_nontext = True
+ node.source = token.source
nodelist.append(node)
def enter_command(self, command, token):
View
4 django/template/debug.py
@@ -55,10 +55,6 @@ def create_nodelist(self):
def create_variable_node(self, contents):
return DebugVariableNode(contents)
- def extend_nodelist(self, nodelist, node, token):
- node.source = token.source
- super(DebugParser, self).extend_nodelist(nodelist, node, token)
-
def unclosed_block_tag(self, parse_until):
command, source = self.command_stack.pop()
msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
View
32 django/template/loader.py
@@ -79,10 +79,9 @@ def reload(self):
return self.loader(self.loadname, self.dirs)[0]
def make_origin(display_name, loader, name, dirs):
- if settings.TEMPLATE_DEBUG and display_name:
+ if display_name:
return LoaderOrigin(display_name, loader, name, dirs)
- else:
- return None
+ return None
def find_template_loader(loader):
if isinstance(loader, (tuple, list)):
@@ -117,7 +116,12 @@ def find_template_loader(loader):
else:
raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader')
-def find_template(name, dirs=None):
+def find_template(name, dirs=None, skip_template=None):
+ """
+ Returns a tuple with a compiled Template object for the given template name,
+ and a origin object. Skipping the current template (skip_template),
+ this param contain the absolute path of the template.
+ """
# Calculate template_source_loaders the first time the function is executed
# because putting this logic in the module-level namespace may cause
# circular import errors. See Django ticket #1292.
@@ -129,20 +133,32 @@ def find_template(name, dirs=None):
if loader is not None:
loaders.append(loader)
template_source_loaders = tuple(loaders)
+ template_candidate = None
for loader in template_source_loaders:
try:
source, display_name = loader(name, dirs)
- return (source, make_origin(display_name, loader, name, dirs))
+ if skip_template and skip_template.endswith(name):
+ extends_tags = source.nodelist[0]
+ extends_tags_origin, extends_tags_source = extends_tags.source
+ if extends_tags_origin.name == skip_template:
+ template_candidate = None
+ continue
+ if not template_candidate:
+ template_candidate = (source, make_origin(display_name, loader, name, dirs))
+ else:
+ return (source, make_origin(display_name, loader, name, dirs))
except TemplateDoesNotExist:
pass
- raise TemplateDoesNotExist(name)
+ if not template_candidate:
+ raise TemplateDoesNotExist(name)
+ return template_candidate
-def get_template(template_name):
+def get_template(template_name, skip_template=None):
"""
Returns a compiled Template object for the given template name,
handling template inheritance recursively.
"""
- template, origin = find_template(template_name)
+ template, origin = find_template(template_name, skip_template=skip_template)
if not hasattr(template, 'render'):
# template needs to be compiled
template = get_template_from_string(template, origin, template_name)
View
5 django/template/loader_tags.py
@@ -94,8 +94,9 @@ def get_parent(self, context):
self.parent_name.token
raise TemplateSyntaxError(error_msg)
if hasattr(parent, 'render'):
- return parent # parent is a Template object
- return get_template(parent)
+ return parent # parent is a Template object
+ origin, source = self.source
+ return get_template(parent, skip_template=origin.name)
def render(self, context):
compiled_parent = self.get_parent(context)
Something went wrong with that request. Please try again.