Commit 9269b606 authored by Chris Beaven's avatar Chris Beaven
Browse files

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

[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
parent 5977193c
Loading
Loading
Loading
Loading
+15 −19
Original line number Diff line number Diff line
@@ -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.
+42 −1
Original line number Diff line number Diff line
@@ -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)
+31 −0
Original line number Diff line number Diff line
@@ -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()