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

拜拜、爱过 提交于 2019-12-08 05:27:10

问题


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


回答1:


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.




回答2:


It seems it isn't possible to this out of the box right now. If you really want to follow this architecture, you will have to write your own custom loader and also figure out a way to pass the request/indicator to let it know its a mobile request.

It's not too tough to write the loader (just see the Django filesystem loader, if the request is from mobile, loop through all the templates_dirs and add the proper suffix to it, so that you include mobile dirs too).

However the biggest challenge as I see it is being able to pass a dynamic parameter to it (indicating that this is a mobile request). You may store this param in the session or modify the template name before passing it to the custom renderer (The renderer will remove this indicator part and get the template) .



来源:https://stackoverflow.com/questions/35863800/mobile-templates-based-on-user-agent-in-django-ensuring-thread-safety

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!