[1.3.X] Fixes regression #15721 -- {% include %} and RequestContext not 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
This commit is contained in:
Chris Beaven 2011-04-22 21:05:29 +00:00
parent 5977193c01
commit 9269b606ba
3 changed files with 88 additions and 20 deletions

View File

@ -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 @@ class BaseContext(object):
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 @@ class Context(BaseContext):
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 File

@ -6,12 +6,15 @@ from django.conf import settings
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 File

@ -17,6 +17,8 @@ from django.template import base as template_base
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 @@ class TemplateTagLoading(unittest.TestCase):
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()