Search code examples
djangodjango-templatesthread-safetymobile-websitedjango-middleware

Mobile templates based on user-agent in django ensuring thread safety


I am developing the mobile version of my website, so thought of using user-agent as the criteria for serving different templates for mobile and web version. I successfully read the user-agent information from nginx and passed it as header to gunicorn server.

Then I created a middleware which reads this header and changes the templates directory in settings file. This seemed to work initially but then I realized that there is race condition happening as this method is not thread safe. (I should have thought of it before-hand).

So I started thinking of other alternatives. One solution was to overwrite the render method of django to include "dirs" parameter based on request header. But then I found out that the "dirs" parameter is deprecated. Following is the reference link https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render So even this will not work.

Another solution is to have different template names for mobile and web and load them accordingly. However I don't want to do this and want to keep the templates directory structure exactly same for both web and mobile.

There has to be a way to just overwrite the template directory. This will give me an advantage of falling back on web version of templates if its missing in mobile templates directory.

Any advise on how to achieve this will be helpful.

This is how my templates are organized.

App1
   templates
       App1
           index.html
           catalog.html
App2
   templates
       App2
           about.html

And in the project directory(not part of the app folder), there is a mobile templates folder which has the following structure

mobile-templates
    App1
        index.html
    App2
        about.html

Thanks Anurag


Solution

  • Here's how I would organize my templates:

    1. Make two directories inside templates dir - mobile and desktop.
    2. Keep mobile templates in mobile dir and desktop templates in desktop.

    This way you won't have to rename the templates.


    And here's how I would render them:

    1. Read User-Agent in a middleware.

    2. Set an attribute on request called template_prefix whose value will either be mobile or desktop, depending on the User-Agent. Eg:

      def process_request(self, request):
          # read user agent and determine if 
          # request is from mobile or desktop
          # ...
          if mobile_user_agent:
              request.template_prefix = 'mobile'
          else:
              request.template_prefix = 'desktop'
      
    3. In your views, use request.template_prefix before template names. Eg:

      def home(request):
          ...
          template = request.template_prefix + '/home.html'
          return render(request, template)
      

    This will render the templates from either mobile or desktop dirs depending on the value template_prefix attribute.


    UPDATE (according to question edit):

    Looking at how your templates are organized, I'd do this:

    1. Middleware:

      Only set template_prefix for mobile requests.

      def process_request(self, request):
          if mobile_user_agent:
              request.template_prefix = 'mobile-templates'
          else:
              request.template_prefix = '' # set an empty string
      
    2. Views:

      Use os.path.join; catch TemplateDoesNotExist exception.

      import os.path
      from django.template.loader import get_template
      from django.template.base import TemplateDoesNotExist
      
      def index(request):
          try:
              template = os.path.join(request.template_prefix, 'App1/index.html')
              get_template(template)
          except TemplateDoesNotExist:
              template = 'App1/index.html'
      
          return render(request, template)
      

    I've tested this and it works. But writing a try...except block in every view seems redundant. If I come up with a better solution, I will update.