Make {{ block.super }} in compress management command a little more robust
- Support multiple {% compress %} calls, with one using {{ block.super }}, inside the same {% block %} - Support more complex inheritance chain
This commit is contained in:
@@ -28,12 +28,62 @@ from compressor.exceptions import OfflineGenerationError
|
|||||||
from compressor.templatetags.compress import CompressorNode
|
from compressor.templatetags.compress import CompressorNode
|
||||||
from compressor.utils import walk, any
|
from compressor.utils import walk, any
|
||||||
|
|
||||||
|
def patched__render(self, context):
|
||||||
|
# 'Fake' _render method that just returns the context instead of rendering.
|
||||||
|
# It also checks whether the first node is an extend node or not, to be able
|
||||||
|
# to handle complex inheritance chain.
|
||||||
|
self._render_firstnode = MethodType(patched__render_firstnode, self)
|
||||||
|
self._render_firstnode(context)
|
||||||
|
return context
|
||||||
|
|
||||||
|
def patched__render_firstnode(self, context):
|
||||||
|
# If this template has a ExtendsNode, we want to find out what
|
||||||
|
# should be put in render_context to make the {% block ... %}
|
||||||
|
# tags work.
|
||||||
|
#
|
||||||
|
# We can't fully render the base template(s) (we don't have the
|
||||||
|
# full context vars - only what's necessary to render the compress
|
||||||
|
# nodes!), therefore we hack the ExtendsNode we found, patching
|
||||||
|
# its get_parent method so that rendering the ExtendsNode only
|
||||||
|
# gives us the blocks content without doing any actual rendering.
|
||||||
|
extra_context = {}
|
||||||
|
firstnode = self.nodelist[0]
|
||||||
|
if isinstance(firstnode, ExtendsNode):
|
||||||
|
firstnode._log = self._log
|
||||||
|
firstnode._log_verbosity = self._log_verbosity
|
||||||
|
firstnode._old_get_parent = firstnode.get_parent
|
||||||
|
firstnode.get_parent = MethodType(patched_get_parent, firstnode)
|
||||||
|
try:
|
||||||
|
extra_context = firstnode.render(context)
|
||||||
|
context.render_context = extra_context.render_context
|
||||||
|
# We aren't rendering {% block %} tags, but we want {{ block.super }}
|
||||||
|
# inside {% compress %} inside {% block %}s to work. Therefore, we
|
||||||
|
# need to pop() the last block context for each block name, to
|
||||||
|
# emulate what would have been done if the {% block %} had been fully
|
||||||
|
# rendered.
|
||||||
|
for blockname in firstnode.blocks.keys():
|
||||||
|
context.render_context[BLOCK_CONTEXT_KEY].pop(blockname)
|
||||||
|
except (IOError, TemplateSyntaxError, TemplateDoesNotExist):
|
||||||
|
# That first node we are trying to render might cause more errors
|
||||||
|
# that we didn't catch when simply creating a Template instance
|
||||||
|
# above, so we need to catch that (and ignore it, just like above)
|
||||||
|
# as well.
|
||||||
|
if self._log_verbosity > 0:
|
||||||
|
self._log.write("Caught error when rendering extend node from \
|
||||||
|
template %s\n" % template.template_name)
|
||||||
|
return None
|
||||||
|
return extra_context
|
||||||
|
|
||||||
def patched_get_parent(self, context):
|
def patched_get_parent(self, context):
|
||||||
# Patch template returned by get_parent to make sure their _render method is
|
# Patch template returned by extendsnode's get_parent to make sure their
|
||||||
# just returning the context instead of actually rendering stuff.
|
# _render method is just returning the context instead of actually
|
||||||
|
# rendering stuff.
|
||||||
|
# In addition, this follows the inheritance chain by looking if the first
|
||||||
|
# node of the template is an extend node itself.
|
||||||
compiled_template = self._old_get_parent(context)
|
compiled_template = self._old_get_parent(context)
|
||||||
compiled_template._render = MethodType(lambda self, c: c, compiled_template)
|
compiled_template._log = self._log
|
||||||
|
compiled_template._log_verbosity = self._log_verbosity
|
||||||
|
compiled_template._render = MethodType(patched__render, compiled_template)
|
||||||
return compiled_template
|
return compiled_template
|
||||||
|
|
||||||
|
|
||||||
@@ -189,32 +239,19 @@ class Command(NoArgsCommand):
|
|||||||
offline_manifest = {}
|
offline_manifest = {}
|
||||||
for template, nodes in compressor_nodes.iteritems():
|
for template, nodes in compressor_nodes.iteritems():
|
||||||
context = Context(settings.COMPRESS_OFFLINE_CONTEXT)
|
context = Context(settings.COMPRESS_OFFLINE_CONTEXT)
|
||||||
extra_context = {}
|
template._log = log
|
||||||
firstnode = template.nodelist[0]
|
template._log_verbosity = verbosity
|
||||||
if isinstance(firstnode, ExtendsNode):
|
template._render_firstnode = MethodType(patched__render_firstnode, template)
|
||||||
# If this template has a ExtendsNode, we apply our patch to
|
extra_context = template._render_firstnode(context)
|
||||||
# generate the necessary context, and then use it for all the
|
if extra_context is None:
|
||||||
# nodes in it, just in case (we don't know which nodes were
|
# Something is wrong - ignore this template
|
||||||
# in a block)
|
continue
|
||||||
firstnode._old_get_parent = firstnode.get_parent
|
|
||||||
firstnode.get_parent = MethodType(patched_get_parent, firstnode)
|
|
||||||
try:
|
|
||||||
extra_context = firstnode.render(context)
|
|
||||||
context.render_context = extra_context.render_context
|
|
||||||
except (IOError, TemplateSyntaxError, TemplateDoesNotExist):
|
|
||||||
# That first node we are trying to render might cause more errors
|
|
||||||
# that we didn't catch when simply creating a Template instance
|
|
||||||
# above, so we need to catch that (and ignore it, just like above)
|
|
||||||
# as well.
|
|
||||||
if verbosity > 0:
|
|
||||||
log.write("Caught error when rendering extend node "
|
|
||||||
"from template %s\n" %
|
|
||||||
template.template_name)
|
|
||||||
continue
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
context.push()
|
context.push()
|
||||||
if extra_context and node._block_name:
|
if extra_context and node._block_name:
|
||||||
context['block'] = context.render_context[BLOCK_CONTEXT_KEY].pop(node._block_name)
|
# Give a block context to the node if it was found inside
|
||||||
|
# a {% block %}.
|
||||||
|
context['block'] = context.render_context[BLOCK_CONTEXT_KEY].get_block(node._block_name)
|
||||||
if context['block']:
|
if context['block']:
|
||||||
context['block'].context = context
|
context['block'].context = context
|
||||||
key = get_offline_hexdigest(node.nodelist.render(context))
|
key = get_offline_hexdigest(node.nodelist.render(context))
|
||||||
|
@@ -5,9 +5,14 @@ from compressor.tests.filters import (CssTidyTestCase, PrecompilerTestCase,
|
|||||||
CssDataUriTestCase, TemplateTestCase)
|
CssDataUriTestCase, TemplateTestCase)
|
||||||
from compressor.tests.jinja2ext import TestJinja2CompressorExtension
|
from compressor.tests.jinja2ext import TestJinja2CompressorExtension
|
||||||
from compressor.tests.offline import (
|
from compressor.tests.offline import (
|
||||||
OfflineGenerationBlockSuperTestCase, OfflineGenerationConditionTestCase,
|
OfflineGenerationBlockSuperTestCase,
|
||||||
OfflineGenerationTemplateTagTestCase, OfflineGenerationTestCaseWithContext,
|
OfflineGenerationBlockSuperTestCaseWithExtraContent,
|
||||||
OfflineGenerationTestCaseErrors, OfflineGenerationTestCase)
|
OfflineGenerationBlockSuperMultipleTestCase,
|
||||||
|
OfflineGenerationConditionTestCase,
|
||||||
|
OfflineGenerationTemplateTagTestCase,
|
||||||
|
OfflineGenerationTestCaseWithContext,
|
||||||
|
OfflineGenerationTestCaseErrors,
|
||||||
|
OfflineGenerationTestCase)
|
||||||
from compressor.tests.parsers import (LxmlParserTests, Html5LibParserTests,
|
from compressor.tests.parsers import (LxmlParserTests, Html5LibParserTests,
|
||||||
BeautifulSoupParserTests, HtmlParserTests)
|
BeautifulSoupParserTests, HtmlParserTests)
|
||||||
from compressor.tests.signals import PostCompressSignalTestCase
|
from compressor.tests.signals import PostCompressSignalTestCase
|
||||||
|
@@ -67,6 +67,25 @@ class OfflineGenerationBlockSuperTestCase(OfflineTestCaseMixin, TestCase):
|
|||||||
templates_dir = "test_block_super"
|
templates_dir = "test_block_super"
|
||||||
expected_hash = "7c02d201f69d"
|
expected_hash = "7c02d201f69d"
|
||||||
|
|
||||||
|
|
||||||
|
class OfflineGenerationBlockSuperMultipleTestCase(OfflineTestCaseMixin, TestCase):
|
||||||
|
templates_dir = "test_block_super_multiple"
|
||||||
|
expected_hash = "2f6ef61c488e"
|
||||||
|
|
||||||
|
|
||||||
|
class OfflineGenerationBlockSuperTestCaseWithExtraContent(OfflineTestCaseMixin, TestCase):
|
||||||
|
templates_dir = "test_block_super_extra"
|
||||||
|
|
||||||
|
def test_offline(self):
|
||||||
|
count, result = CompressCommand().compress(log=self.log, verbosity=self.verbosity)
|
||||||
|
self.assertEqual(2, count)
|
||||||
|
self.assertEqual([
|
||||||
|
u'<script type="text/javascript" src="/media/CACHE/js/ced14aec5856.js"></script>',
|
||||||
|
u'<script type="text/javascript" src="/media/CACHE/js/7c02d201f69d.js"></script>'
|
||||||
|
], result)
|
||||||
|
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT))
|
||||||
|
self.assertEqual(rendered_template, "".join(result) + "\n")
|
||||||
|
|
||||||
|
|
||||||
class OfflineGenerationConditionTestCase(OfflineTestCaseMixin, TestCase):
|
class OfflineGenerationConditionTestCase(OfflineTestCaseMixin, TestCase):
|
||||||
templates_dir = "test_condition"
|
templates_dir = "test_condition"
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
{% spaceless %}
|
||||||
|
{% block js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
alert("test using block.super");
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
{% endspaceless %}
|
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load compress %}
|
||||||
|
|
||||||
|
{% block js %}{% spaceless %}
|
||||||
|
{% compress js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
alert("this alert should be alone.");
|
||||||
|
</script>
|
||||||
|
{% endcompress %}
|
||||||
|
|
||||||
|
{% compress js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
alert("this alert shouldn't be alone!");
|
||||||
|
</script>
|
||||||
|
{% endcompress %}
|
||||||
|
{% endspaceless %}{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}{% endblock %}
|
@@ -0,0 +1,15 @@
|
|||||||
|
{% spaceless %}
|
||||||
|
{% block js %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
alert("test using multiple inheritance and block.super");
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
{% endspaceless %}
|
@@ -0,0 +1,3 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block css %}{% endblock %}
|
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends "base2.html" %}
|
||||||
|
{% load compress %}
|
||||||
|
|
||||||
|
{% block js %}{% spaceless %}
|
||||||
|
{% compress js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
alert("this alert shouldn't be alone!");
|
||||||
|
</script>
|
||||||
|
{% endcompress %}
|
||||||
|
{% endspaceless %}{% endblock %}
|
Reference in New Issue
Block a user