Added support for {{ block.super }}. Dropped support for Django 1.1.X. Fixed #88.

This commit is contained in:
Mathieu Pillard
2011-07-18 18:02:09 +02:00
committed by Jannis Leidel
parent 55ba6a2014
commit fdc05a64de
6 changed files with 87 additions and 40 deletions

View File

@@ -1,5 +1,6 @@
import os import os
import sys import sys
from types import MethodType
from fnmatch import fnmatch from fnmatch import fnmatch
from optparse import make_option from optparse import make_option
@@ -12,6 +13,8 @@ from django.core.management.base import NoArgsCommand, CommandError
from django.template import Context, Template, TemplateDoesNotExist, TemplateSyntaxError from django.template import Context, Template, TemplateDoesNotExist, TemplateSyntaxError
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.template.loader import get_template
from django.template.loader_tags import ExtendsNode, BlockNode, BLOCK_CONTEXT_KEY
try: try:
from django.template.loaders.cached import Loader as CachedLoader from django.template.loaders.cached import Loader as CachedLoader
@@ -25,6 +28,14 @@ from compressor.templatetags.compress import CompressorNode
from compressor.utils import walk, any from compressor.utils import walk, any
def patched_get_parent(self, context):
# Patch template returned by get_parent to make sure their _render method is
# just returning the context instead of actually rendering stuff.
compiled_template = self._old_get_parent(context)
compiled_template._render = MethodType(lambda self, c: c, compiled_template)
return compiled_template
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = "Compress content outside of the request/response cycle" help = "Compress content outside of the request/response cycle"
option_list = NoArgsCommand.option_list + ( option_list = NoArgsCommand.option_list + (
@@ -149,7 +160,8 @@ class Command(NoArgsCommand):
"template %s\n" % template_name) "template %s\n" % template_name)
nodes = list(self.walk_nodes(template)) nodes = list(self.walk_nodes(template))
if nodes: if nodes:
compressor_nodes.setdefault(template_name, []).extend(nodes) template.template_name = template_name
compressor_nodes.setdefault(template, []).extend(nodes)
if not compressor_nodes: if not compressor_nodes:
raise OfflineGenerationError( raise OfflineGenerationError(
@@ -157,14 +169,30 @@ class Command(NoArgsCommand):
if verbosity > 0: if verbosity > 0:
log.write("Found 'compress' tags in:\n\t" + log.write("Found 'compress' tags in:\n\t" +
"\n\t".join(compressor_nodes.keys()) + "\n") "\n\t".join((t.template_name for t in compressor_nodes.keys())) + "\n")
log.write("Compressing... ") log.write("Compressing... ")
count = 0 count = 0
results = [] results = []
for template, nodes in compressor_nodes.iteritems():
context = Context(settings.COMPRESS_OFFLINE_CONTEXT) context = Context(settings.COMPRESS_OFFLINE_CONTEXT)
for nodes in compressor_nodes.values(): extra_context = {}
firstnode = template.nodelist[0]
if isinstance(firstnode, ExtendsNode):
# If this template has a ExtendsNode, we apply our patch to
# generate the necessary context, and then use it for all the
# nodes in it, just in case (we don't know which nodes were
# in a block)
firstnode._old_get_parent = firstnode.get_parent
firstnode.get_parent = MethodType(patched_get_parent, firstnode)
extra_context = firstnode.render(context)
context.render_context = extra_context.render_context
for node in nodes: for node in nodes:
context.push()
if extra_context and node._block_name:
context['block'] = context.render_context[BLOCK_CONTEXT_KEY].pop(node._block_name)
if context['block']:
context['block'].context = context
key = get_offline_cachekey(node.nodelist) key = get_offline_cachekey(node.nodelist)
try: try:
result = node.render(context, forced=True) result = node.render(context, forced=True)
@@ -172,19 +200,22 @@ class Command(NoArgsCommand):
raise CommandError("An error occured during rending: " raise CommandError("An error occured during rending: "
"%s" % e) "%s" % e)
cache.set(key, result, settings.COMPRESS_OFFLINE_TIMEOUT) cache.set(key, result, settings.COMPRESS_OFFLINE_TIMEOUT)
context.pop()
results.append(result) results.append(result)
count += 1 count += 1
log.write("done\nCompressed %d block(s) from %d template(s).\n" % log.write("done\nCompressed %d block(s) from %d template(s).\n" %
(count, len(compressor_nodes))) (count, len(compressor_nodes)))
return count, results return count, results
def walk_nodes(self, node): def walk_nodes(self, node, block_name=None):
for node in getattr(node, "nodelist", []): for node in getattr(node, "nodelist", []):
if (isinstance(node, CompressorNode) or if isinstance(node, BlockNode):
node.__class__.__name__ == "CompressorNode"): # for 1.1.X block_name = node.name
if isinstance(node, CompressorNode):
node._block_name = block_name
yield node yield node
else: else:
for node in self.walk_nodes(node): for node in self.walk_nodes(node, block_name=block_name):
yield node yield node
def handle_extensions(self, extensions=('html',)): def handle_extensions(self, extensions=('html',)):

View File

@@ -0,0 +1,16 @@
{% block content %}{% endblock %}
{% block js%}
<script type="text/javascript">
alert("test 3");
</script>
{% endblock %}
{% block css %}
<style type="text/css">
body {
background: {{ color|default:"purple" }};
}
</style>
{% endblock %}

View File

@@ -1,5 +1,7 @@
{% extends "base.html" %}
{% load compress %} {% load compress %}
{% spaceless %}
{% block content %}{% spaceless %}
{% compress css%} {% compress css%}
<style type="text/css"> <style type="text/css">
body { body {
@@ -19,4 +21,22 @@
alert("test 2"); alert("test 2");
</script> </script>
{% endcompress %} {% endcompress %}
{% endspaceless %} {% endspaceless %}{% endblock %}
{% block js %}{% spaceless %}
{% compress js %}
{{ block.super }}
<script type="text/javascript">
alert("test 4");
</script>
{% endcompress %}
{% endspaceless %}{% endblock %}
{% block css %}{% spaceless %}
{% compress css %}
{{ block.super }}
<style type="text/css">
body { color: orange ; }
</style>
{% endcompress %}
{% endspaceless %}{% endblock %}

View File

@@ -466,11 +466,13 @@ class OfflineGenerationTestCase(TestCase):
def test_offline(self): def test_offline(self):
count, result = CompressCommand().compress() count, result = CompressCommand().compress()
self.assertEqual(3, count) self.assertEqual(5, count)
self.assertEqual([ self.assertEqual([
css_tag('/media/CACHE/css/cd579b7deb7d.css')+'\n', css_tag('/media/CACHE/css/cd579b7deb7d.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>', u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>', u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js" charset="utf-8"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/67ed6aff7f7b.css" type="text/css" />\n',
], result) ], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache # Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves? # key<->values ourselves?
@@ -483,11 +485,13 @@ class OfflineGenerationTestCase(TestCase):
'color': 'blue', 'color': 'blue',
} }
count, result = CompressCommand().compress() count, result = CompressCommand().compress()
self.assertEqual(3, count) self.assertEqual(5, count)
self.assertEqual([ self.assertEqual([
css_tag('/media/CACHE/css/ee62fbfd116a.css')+'\n', css_tag('/media/CACHE/css/ee62fbfd116a.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>', u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>', u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js" charset="utf-8"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/73e015f740c6.css" type="text/css" />\n',
], result) ], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache # Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves? # key<->values ourselves?

View File

@@ -16,6 +16,10 @@ HEAD
To revert to the previous way simply set the ``COMPRESS_CACHE_KEY_FUNCTION`` To revert to the previous way simply set the ``COMPRESS_CACHE_KEY_FUNCTION``
to ``'django_compressor.cache.socket_cachekey'``. to ``'django_compressor.cache.socket_cachekey'``.
- Added support for ``{{ block.super }}`` to ``compress`` management command.
- Dropped Django 1.1.X support.
- Added Compass filter (beta). - Added Compass filter (beta).
- Fixed compiler filters on Windows. - Fixed compiler filters on Windows.

28
tox.ini
View File

@@ -13,34 +13,6 @@ commands =
make clean make clean
make html make html
[testenv:py25-1.1.X]
basepython = python2.5
deps =
unittest2
BeautifulSoup
html5lib
coverage
django==1.1.4
[testenv:py26-1.1.X]
basepython = python2.6
deps =
unittest2
BeautifulSoup
html5lib
coverage
django==1.1.4
[testenv:py27-1.1.X]
basepython = python2.7
deps =
unittest2
BeautifulSoup
html5lib
coverage
django==1.1.4
[testenv:py25-1.2.X] [testenv:py25-1.2.X]
basepython = python2.5 basepython = python2.5
deps = deps =