Opened 17 years ago

Closed 17 years ago

Last modified 16 years ago

#2949 closed enhancement (worksforme)

Django's templating engine too tightly tied to TEMPLATE_DIRS in the settings module

Reported by: lerouxb@… Owned by: nobody
Component: Template system Version: dev
Severity: normal Keywords: tplrf-patched
Cc: Triage Stage: Design decision needed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Django's templating engine is too tightly tied to the settings module. Specifically the loaders. This affects the template loaders as well as what is in django.template.loader_tags.

I want to be able to set the path to a directory that contains the templates I want to render at runtime. Due to the nature of my project, I cannot just add every possibility to TEMPLATE_DIRS and I can definitely not follow a convention based off sub-directories of a specific parent directory. These "skins" (where skin is a directory containing templates) do not live parallel to each other and shouldn't be able to extend each other.

The basic thing I'm looking for is to be able to load a template from a specific, arbitrary path and then tags like extends and include should load templates from the same directory. In fact, I want to add custom filters that can take the name of a template as a parameter to render things with and this should load the relevant template from the correct directory.

I've been looking through the code and django.template.loaders.filesystem.get_template_sources() takes a parameter template_dirs that defaults to None, but this never gets set and it looks like it will always default to settings.TEMPLATE_DIRS.

django.template.loader_tags.ExtendsNode takes a parameter called template_dirs that defaults to None, but this also never gets set and it just uses the loader directly (inside get_parent) to find the template source.

All this means that the template engine is tied to TEMPLATE_DIRS in the settings module and I can't just tell the template loaders to load templates from arbitrary locations and expect the other parts of the system to play along.

Has anyone looked into this before or have ideas on how to fix it? I don't mind writing code, but I'm a bit worried about hacking at the source and then I have to maintain it myself.

I've been considering writing my own loader.py and loader_tags.py that contains a convenience function that will store template_dirs inside the context the first time it is called and then if it gets called without template_dirs (ie from ExtendsNode) it will read the value from the context. It will then reuse the stuff in init.py, context.py, defaultfilters.py and defaulttags.py. Any comments?

Change History (6)

comment:1 by DougN, 17 years ago

I would recommend just writting your own custom loader and adding that to settings.py
I expect to see this request rejected on the grounds that it is easy enough for you to do this your self and the security risks are to high relative to the functionality gained.

comment:2 by lerouxb@…, 17 years ago

Actually, writing another loader will not work, because it will interfere with the other loaders that gets used by other parts of the site and you can't store this type of request-specific state (the directory to load from) anywhere so that when things like the extends tag calls the loader it still loads in from the right place.

I think I'll just make a replacement for loader.py and loader_tags.py rather than add another loader (parallel to filesystem, app_directories, etc). It will be cleaner imho and the rest of my app (like the admin interface) can still use the normal stuff.

Will upload my code somewhere when I'm done with a better explanation of why I had to write it.

comment:3 by Simon G. <dev@…>, 17 years ago

Triage Stage: UnreviewedDesign decision needed

comment:4 by James Bennett, 17 years ago

Resolution: worksforme
Status: newclosed

This _is_ something which can be solved by a custom template loader; nothing precludes having multiple loaders and letting the last one be a "fallback" loader. This is how, for example, Jannis Leidel's database-backed loader works.

comment:5 by anonymous, 17 years ago

ok.. so I have the concept of "skins". A skin is a folder full of templates. Those template files always have the same names.

Let's say I have 3 templates: template_a.html, template_b.html, template_c.html. All of these extend something like base.html and all of them include include.html. There are three skins: skin_a, skin_b, skin_c. A user can select his own skin.

so, I have the following structure:

skins/
skins/skin_a/
skins/skin_a/base.html
skins/skin_a/include.html
skins/skin_a/template_a.html
skins/skin_a/template_b.html
skins/skin_a/template_c.html
skins/skin_b/
skins/skin_b/base.html
skins/skin_b/include.html
skins/skin_b/template_a.html
skins/skin_b/template_b.html
skins/skin_b/template_c.html
skins/skin_c/
skins/skin_c/base.html
skins/skin_c/include.html
skins/skin_c/template_a.html
skins/skin_c/template_b.html
skins/skin_c/template_c.html

(in other words, to make a new skin you just make a copy of skin_a or skin_b or skin_c and change the files - css and images work in a similar way)

I want to be able to just set the skin (or folder) at runtime to something like skins/skin_a/ or skins/skin_b/ or skins/skin_c/ and then use template_a.html or template_b.html or whatever as the template name. So {% extends "base.html" %} or {% include "include.html" %} should just work (in other words - I don't want to have to do {% extends skin_a/base.html %}). Also.. my admin interface is done separately from the "frontend" and it might also have "base.html" or something else that might clash with the other templates. I obviously don't want to mix everything into the same namespace.

I've already solved this problem by making my own "render_template(path, template, ..) function and one or two subclasses, but I'm interested to hear how this will work using a django template loader.

comment:6 by Johannes Dollinger, 16 years ago

Keywords: tplrf-patched added

This would be fixed by #7815.

Note: See TracTickets for help on using tickets.
Back to Top