问题
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