问题
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:
- Make two directories inside
templates
dir -mobile
anddesktop
. - Keep mobile templates in
mobile
dir and desktop templates indesktop
.
This way you won't have to rename the templates.
And here's how I would render them:
- Read User-Agent in a middleware.
Set an attribute on
request
calledtemplate_prefix
whose value will either bemobile
ordesktop
, 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'
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:
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
Views:
Use
os.path.join
; catchTemplateDoesNotExist
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