Proposal: Add current_app to request and pass it to template loaders.

256 views
Skip to first unread message

Tai Lee

unread,
Mar 9, 2011, 7:52:37 PM3/9/11
to Django developers

I have a generic app which includes base templates (for HTML
documents, emails, etc.) and also template tags that render a template
(for form fields, pagination forms/links, filter forms/links, etc.)

Sometimes I have a project that has several other apps installed which
both use the base templates and template tags from the generic app.

Sometimes I need to be able to override the base templates or the
templates rendered by template tags, but only for a single app,
without affecting the other apps.

Currently I believe the only way to override these templates for a
single app only is to pass a list of template names instead of a
single template name to a loader in the views for that app, with the
first template name having a prefix of the current app. But this only
works in my views and my template tags (not with built in template
tags like `extends` and `include`).

I could also duplicate these templates and put them in one of my
TEMPLATE_DIRS, but this would affect all apps within my project.

I would like to propose a few things to make this easier (or possible
at all).

Currently, the `process_view()` method of request middleware is passed
the request, the view function, args and kwargs, which are obtained by
the `resolve()` method, which also returns `url_name`, `app_name` and
`namespace`.

If `namespace` was also passed to `process_view()`, it would be
trivial for users to create a middleware that adds `namespace` to
request objects (or Django could ship with one), which could then
easily be passed through to ``Context`, `RequestContext` and
`TemplateResponse` as `current_app` in the user's views without having
to look it up again (which Django already did with `resolve()`) or
hard code it.

`RequestContext` and `TemplateResponse` could even use
`request.namespace` (if set) as a default for `current_app` (if not
explicitly passed as an argument).

Currently the docs recommend that users pass `current_app` to
`Context`, `RequestContext` and `TemplateResponse`. Why not just do it
for them by default? I can't think of a use case where users would
want to pass a `current_app` that does not match the `namespace` of
the requested URL, but even if they did that would also still be
possible.

If template loaders also accepted a `current_app` argument, which
Django could pass automatically (if set on the context) when using
built in template tags (e.g. `extends`, `include`, etc.), then users
could write their own template loaders that looked for templates in a
folder prefixed with `current_app` first. For example, if
`current_app` on my context is "myapp1" and I do `{% extends "otherapp/
base.html" %}`, it could try to load `['myapp1/otherapp/base.html',
'otherapp/base.html']`.

To summarise:

Ideally I would like Django to automatically add `namespace` (or
`current_app`) to request objects via middleware (and give
`process_view()` access to other attributes returned by `resolve()`),
I would like `RequestContext` and `TemplateResponse` to automatically
use this (if set on `request`, and if `current_app` is not explicitly
passed in as an argument), and I would like built in template tags
like `extends` and `include` to automatically pass `current_app` from
the context (if set) to the template loader(s).

Does anyone have any reason why such a thing would be a bad idea and
likely to be rejected, before I start working on a patch?

If we don't want this to be the default behaviour, the very least I
think we would need to make this feature possible is to allow
`current_app` to be passed as an argument to template loaders and for
built in template tags like `extends` and `include` to pass the
`current_app` (if set on the context).

I can already create a middleware to add `namespace` to `request` (by
resolving `request.path` a second time, not ideal but possible). I can
already add `current_app` to all my `RequestContext` and
`TemplateResponse` objects (but it'd be nicer if Django did it for
me). I can create my own helper function that will return a template
name for the current app, if it exists in the current app's template
folder, given a template name for another app. But I can't create a
template loader that built in template tags like `extends` and
`include` would use to look for templates from the current app's
templates folder. At least, not without resorting to storing the
request in thread locals or something.

Cheers.
Tai.

Doug Ballance

unread,
Mar 10, 2011, 12:58:01 PM3/10/11
to Django developers
I too would very much like a request aware template loader. For my
project the threadlocals approach was the only way I could find to
meet my requirements of a site-based template structure (project has
multitenancy) and user selectable template 'themes'.

I've tried looking at the django source to come up with a non-
threadlocals implementation, but came to the conclusion that it wasn't
easily possible without some kind of global due to the way the
templates are compiled.

Some tags you could potentially add 'hidden' values like __theme__ in
the context using RequestContext and then have the tags pass those on
to get_template(). Assuming tags played nicely and always passed along
those values to their nodelists contexts.

Other tags like {% include "app/sometemplate.html"%} get the template
at compile time of the parent template. The compiled template might
be cached, or otherwise re-used - so that approach isn't reliable.

I hope there is a solution I overlooked.



Dan Fairs

unread,
Mar 11, 2011, 8:17:13 AM3/11/11
to django-d...@googlegroups.com
> Some tags you could potentially add 'hidden' values like __theme__ in
> the context using RequestContext and then have the tags pass those on
> to get_template(). Assuming tags played nicely and always passed along
> those values to their nodelists contexts.
>
> Other tags like {% include "app/sometemplate.html"%} get the template
> at compile time of the parent template. The compiled template might
> be cached, or otherwise re-used - so that approach isn't reliable.
>
> I hope there is a solution I overlooked.

I've implemented something similar to this without threadlocals, using
the new class-based views infrastructure. You can override
TemplateView.get_template_names to return an iterable of template names
(by default it returns [self.template_name], actually defined in
TemplateResponseMixin).

You can then vary what you return based on the request, which is
available as an attribute of the base view class.

Cheers,
Dan

--
Dan Fairs | dan....@gmail.com | www.fezconsulting.com

Tai Lee

unread,
Mar 28, 2011, 12:39:23 AM3/28/11
to Django developers
Now that 1.3 is out, does any core dev have an opinion, feedback or
suggestions on this?

I've solved my immediate need with two template loaders (subclasses of
the app_directories loader) that use thread locals. One prefixes the
requested template name with the app name and the other prefixes it
with the namespace.

But I'd still like to see Django improved so that users can create
template loaders which have access to the current app. It would also
be nice for process_view middleware to have access to the other
attributes of the ResolverMatch object (app name, namespace, etc) to
avoid having to resolve URLs twice.

Cheers.
Tai.

Justin Holmes

unread,
Mar 28, 2011, 1:08:21 AM3/28/11
to django-d...@googlegroups.com
By "current app," do you mean the app which contains the view to which
the current URL is mapped?

> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to django-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
>
>

Tai Lee

unread,
Mar 28, 2011, 8:11:42 AM3/28/11
to Django developers
On Mar 28, 4:08 pm, Justin Holmes <jus...@justinholmes.com> wrote:
> By "current app," do you mean the app which contains the view to which
> the current URL is mapped?

I mean the "namespace" (instance name) for the requested URL or the
"current_app" attribute of a context object which is supposed to be
used as a hint for reversing named URLs. I think it should also be
passed to template loaders as a hint, so that templates can be
overridden for a specific app instance.

digi604

unread,
Jun 13, 2013, 9:48:58 AM6/13/13
to django-d...@googlegroups.com
Was there any movement in this regard?
Reply all
Reply to author
Forward
0 new messages