Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[1.3.X] Fixes regression #15721 -- {% include %} and RequestContext n…

…ot working together. Refs #15814.

Backport of r16031, plus the utility from r16030.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.3.X@16089 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 9269b606ba96d68c3b6be843539bfa6f443c72dc 1 parent 5977193
@SmileyChris SmileyChris authored
View
34 django/template/context.py
@@ -14,21 +14,16 @@ class ContextPopException(Exception):
"pop() has been called more times than push()"
pass
-class EmptyClass(object):
- # No-op class which takes no args to its __init__ method, to help implement
- # __copy__
- pass
-
class BaseContext(object):
def __init__(self, dict_=None):
- dict_ = dict_ or {}
- self.dicts = [dict_]
+ self._reset_dicts(dict_)
+
+ def _reset_dicts(self, value=None):
+ self.dicts = [value or {}]
def __copy__(self):
- duplicate = EmptyClass()
- duplicate.__class__ = self.__class__
- duplicate.__dict__ = self.__dict__.copy()
- duplicate.dicts = duplicate.dicts[:]
+ duplicate = copy(super(BaseContext, self))
+ duplicate.dicts = self.dicts[:]
return duplicate
def __repr__(self):
@@ -78,6 +73,15 @@ def get(self, key, otherwise=None):
return d[key]
return otherwise
+ def new(self, values=None):
+ """
+ Returns a new context with the same properties, but with only the
+ values given in 'values' stored.
+ """
+ new_context = copy(self)
+ new_context._reset_dicts(values)
+ return new_context
+
class Context(BaseContext):
"A stack container for variable context"
def __init__(self, dict_=None, autoescape=True, current_app=None, use_l10n=None):
@@ -99,14 +103,6 @@ def update(self, other_dict):
self.dicts.append(other_dict)
return other_dict
- def new(self, values=None):
- """
- Returns a new Context with the same 'autoescape' value etc, but with
- only the values given in 'values' stored.
- """
- return self.__class__(dict_=values, autoescape=self.autoescape,
- current_app=self.current_app, use_l10n=self.use_l10n)
-
class RenderContext(BaseContext):
"""
A stack container for storing Template state.
View
43 django/test/utils.py
@@ -6,12 +6,15 @@
from django.core import mail
from django.core.mail.backends import locmem
from django.test import signals
-from django.template import Template
+from django.template import Template, loader, TemplateDoesNotExist
+from django.template.loaders import cached
from django.utils.translation import deactivate
__all__ = ('Approximate', 'ContextList', 'setup_test_environment',
'teardown_test_environment', 'get_runner')
+RESTORE_LOADERS_ATTR = '_original_template_source_loaders'
+
class Approximate(object):
def __init__(self, val, places=7):
@@ -125,3 +128,41 @@ def get_runner(settings):
test_module = __import__(test_module_name, {}, {}, test_path[-1])
test_runner = getattr(test_module, test_path[-1])
return test_runner
+
+
+def setup_test_template_loader(templates_dict, use_cached_loader=False):
+ """
+ Changes Django to only find templates from within a dictionary (where each
+ key is the template name and each value is the corresponding template
+ content to return).
+
+ Use meth:`restore_template_loaders` to restore the original loaders.
+ """
+ if hasattr(loader, RESTORE_LOADERS_ATTR):
+ raise Exception("loader.%s already exists" % RESTORE_LOADERS_ATTR)
+
+ def test_template_loader(template_name, template_dirs=None):
+ "A custom template loader that loads templates from a dictionary."
+ try:
+ return (templates_dict[template_name], "test:%s" % template_name)
+ except KeyError:
+ raise TemplateDoesNotExist(template_name)
+
+ if use_cached_loader:
+ template_loader = cached.Loader(('test_template_loader',))
+ template_loader._cached_loaders = (test_template_loader,)
+ else:
+ template_loader = test_template_loader
+
+ setattr(loader, RESTORE_LOADERS_ATTR, loader.template_source_loaders)
+ loader.template_source_loaders = (template_loader,)
+ return template_loader
+
+
+def restore_template_loaders():
+ """
+ Restores the original template loaders after
+ :meth:`setup_test_template_loader` has been run.
+ """
+ loader.template_source_loaders = getattr(loader, RESTORE_LOADERS_ATTR)
+ delattr(loader, RESTORE_LOADERS_ATTR)
View
31 tests/regressiontests/templates/tests.py
@@ -17,6 +17,8 @@
from django.core import urlresolvers
from django.template import loader
from django.template.loaders import app_directories, filesystem, cached
+from django.test.utils import setup_test_template_loader,\
+ restore_template_loaders
from django.utils import unittest
from django.utils.translation import activate, deactivate, ugettext as _
from django.utils.safestring import mark_safe
@@ -1640,5 +1642,34 @@ def test_load_working_egg(self):
settings.INSTALLED_APPS = ('tagsegg',)
t = template.Template(ttext)
+
+class RequestContextTests(BaseTemplateResponseTest):
+
+ def setUp(self):
+ templates = {
+ 'child': Template('{{ var|default:"none" }}'),
+ }
+ setup_test_template_loader(templates)
+ self.fake_request = RequestFactory().get('/')
+
+ def tearDown(self):
+ restore_template_loaders()
+
+ def test_include_only(self):
+ """
+ Regression test for #15721, ``{% include %}`` and ``RequestContext``
+ not playing together nicely.
+ """
+ ctx = RequestContext(self.fake_request, {'var': 'parent'})
+ self.assertEqual(
+ template.Template('{% include "child" %}').render(ctx),
+ 'parent'
+ )
+ self.assertEqual(
+ template.Template('{% include "child" only %}').render(ctx),
+ 'none'
+ )
+
+
if __name__ == "__main__":
unittest.main()
Please sign in to comment.
Something went wrong with that request. Please try again.