Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing request to custom Django template loader

I want to write custom template loader for my Django app which looks for a specific folder based on a key that is part of the request.

Let me get into more details to be clear. Assume that I will be getting a key on every request(which I populate using a middleware).

Example: request.key could be 'india' or 'usa' or 'uk'.

I want my template loader to look for the template "templates/<key>/<template.html>". So when I say {% include "home.html" %}, I want the template loader to load "templates/india/home.html" or "templates/usa/home.html" or "templates/uk/home.html" based on the request.

Is there a way to pass the request object to a custom template loader?

like image 310
UnTechie Avatar asked Mar 20 '23 21:03

UnTechie


2 Answers

I've been searching for the same solution and, after a couple days of searching, decided to use threading.local(). Simply make the request object global for the duration of the HTTP request processing! Commence rotten tomato throwing from the gallery.

Let me explain:

As of Django 1.8 (according to the development version docs) the "dirs" argument for all template finding functions will be deprecated. (ref)

This means that there are no arguments passed into a custom template loader other than the template name being requested and the list of template directories. If you want to access paramters in the request URL (or even the session information) you'll have to "reach out" into some other storage mechanism.

import threading
_local = threading.local()

class CustomMiddleware:

    def process_request(self, request):
         _local.request = request

def load_template_source(template_name, template_dirs=None):
    if _local.request:
        # Get the request URL and work your magic here!
        pass

In my case it wasn't the request object (directly) I was after but rather what site (I'm developing a SaaS solution) the template should be rendered for.

like image 146
rob Avatar answered Mar 22 '23 11:03

rob


To find the template to render Django uses the get_template method which only gets the template_name and optional dirs argument. So you cannot really pass the request there.

However, if you customize your render_to_response function to pass along a dirs argument you should be able to do it.

For example (assuming you are using a RequestContext as most people would):

from django import shortcuts
from django.conf import settings

def render_to_response(template_name, dictionary=None, context_instance=None, content_type=None, dirs):
    assert context_instance, 'This method requires a `RequestContext` instance to function'
    if not dirs:
        dirs = []
    dirs.append(os.path.join(settings.BASE_TEMPLATE_DIR, context_instance['request'].key)
    return shortcuts.render_to_response(template_name, dictionary, context_instance, content_type, dirs)
like image 42
Wolph Avatar answered Mar 22 '23 12:03

Wolph