Opened 7 years ago

Closed 7 years ago

Last modified 7 years ago

#28491 closed Uncategorized (invalid)

"TypeError: context must be a dict rather than Context" when using loader.select_template() with Context

Reported by: Mark Jones Owned by: nobody
Component: Template system Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The render method of the Template class looks like this

    def render(self, context=None, request=None):
        context = make_context(context, request, autoescape=self.backend.engine.autoescape)
        try:
            return self.template.render(context)
        except TemplateDoesNotExist as exc:
            reraise(exc, self.backend)

The docs show this:

>>> from django.template import Context, Template
>>> template = Template("My name is {{ my_name }}.")

>>> context = Context({"my_name": "Adrian"})
>>> template.render(context)
"My name is Adrian."

>>> context = Context({"my_name": "Dolores"})
>>> template.render(context)
"My name is Dolores."

However make_context looks like this (and throws an exception because of it):

    if context is not None and not isinstance(context, dict):
        raise TypeError('context must be a dict rather than %s.' % context.__class__.__name__)
    if request is None:
        context = Context(context, **kwargs)
    else:
        # The following pattern is required to ensure values from
        # context override those from template context processors.
        original_context = context
        context = RequestContext(request, **kwargs)
        if original_context:
            context.push(original_context)
    return context

I think it needs this code placed before the original code in make_context

    if isinstance(context, Context):
        return context

so that if an actual context is passed in, it works as documented.

This bug only comes about when you select a template using code that looks like this:

from django.template import loader, Context
            servertemplate = loader.select_template(["deployment/%s" % options['webserver'],
                                                     "deployment/default_%s" % options['webserver']])

because the loader returns an instance of <class 'django.template.backends.django.Template'> instead of <class 'django.template.base.Template'> which has a different render method that causes this problem.

My change does fix the issue, but I'm not sure if this is the right fix, or if there should be another.

Change History (3)

comment:1 by Tim Graham, 7 years ago

To fix your code, you should indeed call render() with a plaint dict rather than a Context.

This is related to a change in Django 1.11 (#27258). Does some documentation need clarification?

comment:2 by Tim Graham, 7 years ago

Resolution: invalid
Status: newclosed

I see you moved the conversation to ticket:27258#comment:12 and 13. Another possibility to fix your code would be to adapt the approach from #27722 like this:

servertemplate = loader.select_template(...).template

This won't work if you use any template engine besides the DjangoTemplates but your comment suggests you don't care about that.

If you have some other suggestion of a code or documentation change to make, feel free to reopen the ticket.

comment:3 by Tim Graham, 7 years ago

Summary: TypeError: context must be a dict rather than Context."TypeError: context must be a dict rather than Context" when using loader.select_template() with Context
Note: See TracTickets for help on using tickets.
Back to Top