Django - use custom template loader on a per-request basis?

*爱你&永不变心* 提交于 2019-12-24 03:19:07

问题


Is there a lower-level way to provide the list of loaders when rendering a template, as opposed to always having Django use the setting?

I'd like to use a custom template loader instance for only a few views (I have my reasons).


回答1:


It looks like you'll have to write some code of your own to do it. Let's take a look at the normal code path for loading templates, if you use, say, render_to_response, where the relevant part of the source is:

return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)

That's a call to django.template.loader.render_to_string, which passes through some other functions and eventually ends up calling find_template.

The first time find_template is called, in initializes the global template_source_loaders cache based on settings.TEMPLATE_LOADERS. So it looks like there's no just extra argument you can pass in or anything like that.

One possibility might be to add some loaders to django.template.loader.template_source_loaders just for the duration of that view. I don't know if that will cause other problems; it feels dirty, but if it works, it'll be pretty easy. (Just make a view decorator that does it.)

If you don't want to do that, it looks like you'll have to replicate the work of render_to_string with your own code (if you're really sure you want to use per-view template loaders, which I'm accepting as a premise for the sake of this question but I bet isn't actually necessary). There's not all that much code there, and if you know in advance a specific loader and a single template name that you want to use, it's actually pretty easy. (This is untested but will probably pretty much work.)

 def render_to_response_with_loader(loader, name,
           dictionary=None, context_instance=None, mimetype=None, dirs=None):

    # from find_template
    t, display_name = loader(name, dirs)

    # from get_template
    if not hasattr(t, 'render'):
        # template needs to be compiled
        t = django.template.loader.get_template_from_string(t, origin, template_name)

    # from render_to_string
    if not context_instance:
        rendered = t.render(Context(dictionary))
    else:
        # Add the dictionary to the context stack, ensuring it gets removed again
        # to keep the context_instance in the same state it started in.
        context_instance.update(dictionary)
        try:
            rendered = t.render(context_instance)
        finally:
            context_instance.pop()

     # from render_to_response
     return HttpResponse(rendered, mimetype=mimetype)

If you want to support multiple possible loaders or a list of possible filenames, just copy the relevant code from django.template.loader.




回答2:


I ended up doing this by modifying template_source_loaders as Dougal suggested. Like he said, I'm not sure this is safe (could it create a race condition?), but it works for my particular case at the moment. The benefit of doing it this way over the other way Dougal suggested is that it makes sure that {% extends %} and {% include %} also use the modified loaders. Here's my render_to_string with custom loaders:

def render_to_string_with_loader(*args, **kwargs):
    """ Call render_to_string using ReportTemplateLoader to find templates. """
    import django.template.loader as loader
    old_loaders = settings.TEMPLATE_LOADERS
    settings.TEMPLATE_LOADERS = ('main.loaders.ReportTemplateLoader',)
    loader.template_source_loaders = None # force refresh from settings
    try:
        out = render_to_string(*args, **kwargs)
    finally:
        # use finally make sure template errors can't mess up later requests
        settings.TEMPLATE_LOADERS = old_loaders
        loader.template_source_loaders = None


来源:https://stackoverflow.com/questions/11038610/django-use-custom-template-loader-on-a-per-request-basis

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!