mirror of
				https://github.com/django/django.git
				synced 2025-10-29 00:26:07 +00:00 
			
		
		
		
	[1.8.x] Set context.template instead of context.engine while rendering.
This opens more possibilities, like accessing context.template.origin.
It also follows the chain of objects instead of following a shortcut.
Backport of 1bfcc95 from master
			
			
This commit is contained in:
		| @@ -202,19 +202,19 @@ class Template(object): | ||||
|  | ||||
|     def render(self, context): | ||||
|         "Display stage -- can be called many times" | ||||
|         # Set engine attribute here to avoid changing the signature of either | ||||
|         # Context.__init__ or Node.render. The engine is set only on the first | ||||
|         # call to render. Further calls e.g. for includes don't override it. | ||||
|         toplevel_render = context.engine is None | ||||
|         # Set context.template to the original template -- as opposed to | ||||
|         # extended or included templates -- during rendering. This may be | ||||
|         # used for accessing context.template.engine. | ||||
|         toplevel_render = context.template is None | ||||
|         if toplevel_render: | ||||
|             context.engine = self.engine | ||||
|             context.template = self | ||||
|         context.render_context.push() | ||||
|         try: | ||||
|             return self._render(context) | ||||
|         finally: | ||||
|             context.render_context.pop() | ||||
|             if toplevel_render: | ||||
|                 context.engine = None | ||||
|                 context.template = None | ||||
|  | ||||
|  | ||||
| class Token(object): | ||||
| @@ -653,7 +653,7 @@ class FilterExpression(object): | ||||
|                 if ignore_failures: | ||||
|                     obj = None | ||||
|                 else: | ||||
|                     string_if_invalid = context.engine.string_if_invalid | ||||
|                     string_if_invalid = context.template.engine.string_if_invalid | ||||
|                     if string_if_invalid: | ||||
|                         if '%s' in string_if_invalid: | ||||
|                             return string_if_invalid % self.var | ||||
| @@ -845,7 +845,7 @@ class Variable(object): | ||||
|                     if getattr(current, 'do_not_call_in_templates', False): | ||||
|                         pass | ||||
|                     elif getattr(current, 'alters_data', False): | ||||
|                         current = context.engine.string_if_invalid | ||||
|                         current = context.template.engine.string_if_invalid | ||||
|                     else: | ||||
|                         try:  # method call (assuming no args required) | ||||
|                             current = current() | ||||
| @@ -853,12 +853,12 @@ class Variable(object): | ||||
|                             try: | ||||
|                                 getcallargs(current) | ||||
|                             except TypeError:  # arguments *were* required | ||||
|                                 current = context.engine.string_if_invalid  # invalid method call | ||||
|                                 current = context.template.engine.string_if_invalid  # invalid method call | ||||
|                             else: | ||||
|                                 raise | ||||
|         except Exception as e: | ||||
|             if getattr(e, 'silent_variable_failure', False): | ||||
|                 current = context.engine.string_if_invalid | ||||
|                 current = context.template.engine.string_if_invalid | ||||
|             else: | ||||
|                 raise | ||||
|  | ||||
| @@ -1275,9 +1275,9 @@ class Library(object): | ||||
|                         elif isinstance(getattr(file_name, 'template', None), Template): | ||||
|                             t = file_name.template | ||||
|                         elif not isinstance(file_name, six.string_types) and is_iterable(file_name): | ||||
|                             t = context.engine.select_template(file_name) | ||||
|                             t = context.template.engine.select_template(file_name) | ||||
|                         else: | ||||
|                             t = context.engine.get_template(file_name) | ||||
|                             t = context.template.engine.get_template(file_name) | ||||
|                         self.nodelist = t.nodelist | ||||
|                     new_context = context.new(_dict) | ||||
|                     # Copy across the CSRF token, if present, because | ||||
|   | ||||
| @@ -123,7 +123,7 @@ class Context(BaseContext): | ||||
|     "A stack container for variable context" | ||||
|     def __init__(self, dict_=None, autoescape=True, | ||||
|             current_app=_current_app_undefined, | ||||
|             use_l10n=None, use_tz=None, engine=None): | ||||
|             use_l10n=None, use_tz=None): | ||||
|         if current_app is not _current_app_undefined: | ||||
|             warnings.warn( | ||||
|                 "The current_app argument of Context is deprecated. Use " | ||||
| @@ -133,8 +133,10 @@ class Context(BaseContext): | ||||
|         self._current_app = current_app | ||||
|         self.use_l10n = use_l10n | ||||
|         self.use_tz = use_tz | ||||
|         self.engine = engine | ||||
|         self.render_context = RenderContext() | ||||
|         # Set to the original template during rendering -- as opposed to | ||||
|         # extended or included templates | ||||
|         self.template = None | ||||
|         super(Context, self).__init__(dict_) | ||||
|  | ||||
|     @property | ||||
| @@ -192,11 +194,11 @@ class RequestContext(Context): | ||||
|     """ | ||||
|     def __init__(self, request, dict_=None, processors=None, | ||||
|             current_app=_current_app_undefined, | ||||
|             use_l10n=None, use_tz=None, engine=None): | ||||
|             use_l10n=None, use_tz=None): | ||||
|         # current_app isn't passed here to avoid triggering the deprecation | ||||
|         # warning in Context.__init__. | ||||
|         super(RequestContext, self).__init__( | ||||
|             dict_, use_l10n=use_l10n, use_tz=use_tz, engine=engine) | ||||
|             dict_, use_l10n=use_l10n, use_tz=use_tz) | ||||
|         if current_app is not _current_app_undefined: | ||||
|             warnings.warn( | ||||
|                 "The current_app argument of RequestContext is deprecated. " | ||||
| @@ -207,23 +209,27 @@ class RequestContext(Context): | ||||
|         self._processors = () if processors is None else tuple(processors) | ||||
|         self._processors_index = len(self.dicts) | ||||
|         self.update({})         # placeholder for context processors output | ||||
|         self.engine = engine    # re-run the setter in case engine is not None | ||||
|  | ||||
|     @property | ||||
|     def engine(self): | ||||
|         return self._engine | ||||
|     def template(self): | ||||
|         return self._template | ||||
|  | ||||
|     @engine.setter | ||||
|     def engine(self, engine): | ||||
|         self._engine = engine | ||||
|     @template.setter | ||||
|     def template(self, template): | ||||
|         # Execute context processors when Template.render(self, context) sets | ||||
|         # context.template = self. Until then, since the context isn't tied to | ||||
|         # an engine, it has no way to know which context processors to apply. | ||||
|         self._template = template | ||||
|         if hasattr(self, '_processors_index'): | ||||
|             if engine is None: | ||||
|             if template is None: | ||||
|                 # Unset context processors. | ||||
|                 self.dicts[self._processors_index] = {} | ||||
|             else: | ||||
|                 # Set context processors for this engine. | ||||
|                 processors = (template.engine.template_context_processors + | ||||
|                               self._processors) | ||||
|                 updates = {} | ||||
|                 for processor in engine.template_context_processors + self._processors: | ||||
|                 for processor in processors: | ||||
|                     updates.update(processor(self.request)) | ||||
|                 self.dicts[self._processors_index] = updates | ||||
|  | ||||
|   | ||||
| @@ -211,7 +211,7 @@ class ForNode(Node): | ||||
|                     context[self.loopvars[0]] = item | ||||
|                 # In debug mode provide the source of the node which raised | ||||
|                 # the exception | ||||
|                 if context.engine.debug: | ||||
|                 if context.template.engine.debug: | ||||
|                     for node in self.nodelist_loop: | ||||
|                         try: | ||||
|                             nodelist.append(node.render(context)) | ||||
| @@ -392,7 +392,7 @@ class SsiNode(Node): | ||||
|     def render(self, context): | ||||
|         filepath = self.filepath.resolve(context) | ||||
|  | ||||
|         if not include_is_allowed(filepath, context.engine.allowed_include_roots): | ||||
|         if not include_is_allowed(filepath, context.template.engine.allowed_include_roots): | ||||
|             if settings.DEBUG: | ||||
|                 return "[Didn't have permission to include file]" | ||||
|             else: | ||||
| @@ -404,7 +404,7 @@ class SsiNode(Node): | ||||
|             output = '' | ||||
|         if self.parsed: | ||||
|             try: | ||||
|                 t = Template(output, name=filepath, engine=context.engine) | ||||
|                 t = Template(output, name=filepath, engine=context.template.engine) | ||||
|                 return t.render(context) | ||||
|             except TemplateSyntaxError as e: | ||||
|                 if settings.DEBUG: | ||||
|   | ||||
| @@ -107,7 +107,7 @@ class ExtendsNode(Node): | ||||
|         if isinstance(getattr(parent, 'template', None), Template): | ||||
|             # parent is a django.template.backends.django.Template | ||||
|             return parent.template | ||||
|         return context.engine.get_template(parent) | ||||
|         return context.template.engine.get_template(parent) | ||||
|  | ||||
|     def render(self, context): | ||||
|         compiled_parent = self.get_parent(context) | ||||
| @@ -148,7 +148,7 @@ class IncludeNode(Node): | ||||
|             # Does this quack like a Template? | ||||
|             if not callable(getattr(template, 'render', None)): | ||||
|                 # If not, we'll try get_template | ||||
|                 template = context.engine.get_template(template) | ||||
|                 template = context.template.engine.get_template(template) | ||||
|             values = { | ||||
|                 name: var.resolve(context) | ||||
|                 for name, var in six.iteritems(self.extra_context) | ||||
| @@ -158,7 +158,7 @@ class IncludeNode(Node): | ||||
|             with context.push(**values): | ||||
|                 return template.render(context) | ||||
|         except Exception: | ||||
|             if context.engine.debug: | ||||
|             if context.template.engine.debug: | ||||
|                 raise | ||||
|             return '' | ||||
|  | ||||
|   | ||||
| @@ -149,7 +149,7 @@ class BlockTranslateNode(Node): | ||||
|                 result = translation.pgettext(message_context, singular) | ||||
|             else: | ||||
|                 result = translation.ugettext(singular) | ||||
|         default_value = context.engine.string_if_invalid | ||||
|         default_value = context.template.engine.string_if_invalid | ||||
|  | ||||
|         def render_value(key): | ||||
|             if key in context: | ||||
|   | ||||
| @@ -772,10 +772,11 @@ Notes: | ||||
| * The ``render()`` method is where the work actually happens. | ||||
|  | ||||
| * ``render()`` should generally fail silently, particularly in a production | ||||
|   environment. In some cases however, particularly if ``context.engine.debug`` | ||||
|   is ``True``, this method may raise an exception to make debugging easier. | ||||
|   For example, several core tags raise ``django.template.TemplateSyntaxError`` | ||||
|   if they receive the wrong number or type of arguments. | ||||
|   environment. In some cases however, particularly if | ||||
|   ``context.template.engine.debug`` is ``True``, this method may raise an | ||||
|   exception to make debugging easier. For example, several core tags raise | ||||
|   ``django.template.TemplateSyntaxError`` if they receive the wrong number or | ||||
|   type of arguments. | ||||
|  | ||||
| Ultimately, this decoupling of compilation and rendering results in an | ||||
| efficient template system, because a template can render multiple contexts | ||||
| @@ -811,16 +812,17 @@ This is not a very common situation, but it's useful if you're rendering a | ||||
| template yourself. For example:: | ||||
|  | ||||
|     def render(self, context): | ||||
|         t = context.engine.get_template('small_fragment.html') | ||||
|         t = context.template.engine.get_template('small_fragment.html') | ||||
|         return t.render(Context({'var': obj}, autoescape=context.autoescape)) | ||||
|  | ||||
| .. versionchanged:: 1.8 | ||||
|  | ||||
|     The ``engine`` attribute of ``Context`` objects was added in Django 1.8. | ||||
|     :meth:`context.engine.get_template <django.template.Engine.get_template>` | ||||
|     must be used instead of :func:`django.template.loader.get_template` | ||||
|     because the latter now returns a wrapper whose ``render`` method doesn't | ||||
|     accept a :class:`~django.template.Context`. | ||||
|     The ``template`` attribute of ``Context`` objects was added in Django 1.8. | ||||
|     :meth:`context.template.engine.get_template | ||||
|     <django.template.Engine.get_template>` must be used instead of | ||||
|     :func:`django.template.loader.get_template` because the latter now returns | ||||
|     a wrapper whose ``render`` method doesn't accept a | ||||
|     :class:`~django.template.Context`. | ||||
|  | ||||
| If we had neglected to pass in the current ``context.autoescape`` value to our | ||||
| new ``Context`` in this example, the results would have *always* been | ||||
|   | ||||
| @@ -162,7 +162,7 @@ instance in the ``render()`` method of a template tag, you can use the current | ||||
|  | ||||
| You can write:: | ||||
|  | ||||
|     template = context.engine.get_template('included.html') | ||||
|     template = context.template.engine.get_template('included.html') | ||||
|  | ||||
| This will load the template with the current engine without triggering the | ||||
| multiple template engines machinery, which is usually the desired behavior. | ||||
| @@ -201,7 +201,7 @@ APIs. The multiple template engines machinery isn't involved here. | ||||
| Finally, if you have access to the current context, you can use the same trick | ||||
| as above:: | ||||
|  | ||||
|     template = context.engine.from_string(template_code) | ||||
|     template = context.template.engine.from_string(template_code) | ||||
|  | ||||
| ``Template()`` | ||||
| ============== | ||||
|   | ||||
| @@ -516,9 +516,6 @@ class RequestContextTests(unittest.TestCase): | ||||
|         self.assertEqual(len(ctx.dicts), 3) | ||||
|  | ||||
|     def test_context_comparable(self): | ||||
|         # Create an engine without any context processors. | ||||
|         engine = Engine() | ||||
|  | ||||
|         test_data = {'x': 'y', 'v': 'z', 'd': {'o': object, 'a': 'b'}} | ||||
|  | ||||
|         # test comparing RequestContext to prevent problems if somebody | ||||
| @@ -526,8 +523,8 @@ class RequestContextTests(unittest.TestCase): | ||||
|         request = RequestFactory().get('/') | ||||
|  | ||||
|         self.assertEqual( | ||||
|             RequestContext(request, dict_=test_data, engine=engine), | ||||
|             RequestContext(request, dict_=test_data, engine=engine)) | ||||
|             RequestContext(request, dict_=test_data), | ||||
|             RequestContext(request, dict_=test_data)) | ||||
|  | ||||
|  | ||||
| @ignore_warnings(category=RemovedInDjango20Warning) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user