1

I would like to be able to render some or all of the views in my project with a different base template. In other words, for url /some/view I would like to be able to have /inline/some/view and have it render the same content, but using a different base template.

Modifying each view to accept a different template is not an option, because I would like to apply this behaviour across all apps in the project, including things like django.contrib.auth.

So far, I have, in urls.py:

url("^inline/(?P<uri>.*)", format.inline, name='inline'),

And the view, format.py:

from django.core.urlresolvers import resolve

def inline(request, uri=''):

    # get the view that would normally handle this request
    view, args, kwargs = resolve('/' + uri)

    # call the view
    kwargs['request'] = request
    template_response = view(*args, **kwargs)

    # ...now what?

I'm not sure where to go from here. Can I modify the entire template chain before I call view(), so that template_response.render() does the right thing?

Perhaps I am entirely off-base with this approach and should be looking at a middleware solution, but I am attached to the idea of this behaviour keying off URLs, because it will be easy to explain to the content people later on.

UPDATE

I was able to achieve the effect I desired, but the implementation is severely lacking. Here's what I did:

  • copied the templates for the views I wished to inline into templates/inline/
  • replaced {% extends base.html %} with {% extends inline/base.html %}
  • modified the view thus:

    from django.core.urlresolvers import resolve
    
    def inline(request, uri=''):
    
        # get the view that would normally handle this request
        view, args, kwargs = resolve('/' + uri)
    
        # call the view
        kwargs['request'] = request
        template_response = view(*args, **kwargs)
        response.template_name = os.path.join('inline', response.template_name)
        return response
    

I don't like this solution because it will require those inline templates to be managed, being replaced/updated whenever apps in the project change, and so on. I would still dearly love a cleaner solution.

Update 2: Solution

chris-wesseling was 100% correct; a custom template loader was exactly what I needed. For posterity, here is my implementation.

app/loaders.py:

from django.conf import settings
from django.template.loader import BaseLoader
from django.template.base import TemplateDoesNotExist
import os


class BaseTemplateOverrideLoader(BaseLoader):
    """
    Load templates from a specified subdirectory in the current app's directory.
    """
    subdir = 'templates'

    def load_template_source(self, template_name, template_dirs=None):

        template_dir = os.path.join(
            os.path.dirname(os.path.realpath(__file__)),
            self.subdir
        )

        try:
            t = os.path.join(template_dir, template_name)
            with open(t, 'rb') as fp:
                return (fp.read().decode(settings.FILE_CHARSET), template_dir)
        except IOError:
            pass
        raise TemplateDoesNotExist(template_name)


class InlineTemplateLoader(BaseTemplateOverrideLoader):
    """
    Override the location of base.html for inline views.
    """
    is_usable = True
    subdir = 'templates/inline'

# ... other custom override classes here ....

app/views/inline.py:

from django.conf import settings
from django.core.urlresolvers import resolve
from django.template import loader


def default(request, slug=None):

    view, args, kwargs = resolve('/' + slug)

    old_loaders = settings.TEMPLATE_LOADERS

    # Temporarily insert the inline template loader into TEMPLATE_LOADERS;
    # we must also force BaseLoader to reload all templates loaders since
    # they are cached at compile time.
    settings.TEMPLATE_LOADERS = ('app.loaders.InlineTemplateLoader', ) + \
        settings.TEMPLATE_LOADERS
    loader.template_source_loaders = None

    # now call the original view that handles this request
    kwargs['request'] = request
    response = view(*args, **kwargs)
    response_string = response.render()

    # restore the template loaders to their original condition
    settings.TEMPLATE_LOADERS = old_loaders
    loader.template_source_loaders = None

    return response_string

app/templates/inline/base.html:

{% comment %}
inline/base.html
    -- render just the main content without any styles etc,
       for loading as inline content via ajax or whatever.
{% endcomment %}
{% block main %}{% endblock %}
4
  • 1
    Maybe this helps: docs.djangoproject.com/en/dev/ref/templates/builtins/#extends {% extends variable %} uses the value of variable. If the variable evaluates to a string, Django will use that string as the name of the parent template. If the variable evaluates to a Template object, Django will use that object as the parent template. Commented Apr 9, 2014 at 17:45
  • I don't think there's a way to accomplish this with template inheritance since by the time you get to the template you're already in the wrong place. I need to modify the template chain before rendering, I think. Commented Apr 9, 2014 at 17:54
  • 1
    Oh ho! I misunderstood. I think you may be on to something, if I add a template object to the requestcontext, and have my base template {% extend template_object %}, it might be just the thing. Commented Apr 9, 2014 at 18:04
  • No dice; {% extends %} in base.html causes an infinite recursion error(which makes sense when I stop and think about it). Commented Apr 9, 2014 at 19:41

1 Answer 1

1

You can implement your own TemplateLoader and set it in your settings.TEMPLATE_LOADERS. You can have a look at this similar question for an approach of what you're trying to do.

Basically what you're looking for is a way to load base.html from a different location.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.