EDIT: The code in this post has been refactored and packaged here, but this post explains how I solved my problem.

What is the proper way to handle browser traffic redirection and/or experience for mobile users?

I wanted to try out a simple version of LMD using jquery mobile. The current website is written using Django. All the business logic would remain the same, I just want to use different templates when the request comes from a mobile browser.

The solution I chose:

write some middleware that would identify all incoming mobile requests

use a custom template loader that returns a mobile template if its a mobile user

Some people may want to actually redirect their users to a different url or subdomain if they find a mobile request, which you can easily do with this middleware if you’d like. Just do the redirection where it says #this is a mobile request.

You’ll notice I’ve also added my request object to a thread local varible (Please someone freak out and tell me why this is going to be my demise in the comments). If you are going to be redirecting users, you won’t need any of the thread local stuff. I am using it here because it allows me to easily get access to the request later on in my template loader as you’ll see below.

I chose setting a flag on the request object because LMD lives on AppEngine, so managing subdomains is not as easy as it would be using apache. But as far as I saw it, I have to run the regex on every request into my main domain to check for mobile anyways. Plus, I don’t really want a different app to handle my mobile requests, I just want to render a different template (writing your own template loader).

The one I wrote is based off django’s primary template loader, found on a post by Corey Oordt. It is super simple and only pulls from a single directory called mobile_templates. You can easily expand on this to have a tuple of directories in your settings file to loop through, much like TEMPLATE_DIRS works.

This loader will get the current request via the get_current_request() method I defined in my middleware (via the thread local varible). If it is a mobile request, I attempt to open the template from my mobile_templates folder. If a mobile template is not found it returns a TemplateDoesNotExist exception and django will continue on with other loaders until it finds a matching template. This is nice because it fails back to my default templates when I do not have a specific mobile version.

Another reason I set the request.is_mobile flag in the middleware is that it allows me to do specific mobile business logic in my app if I want to. I used it to add a mobile flag to my context dictionary so it can be used in any templates (in the case where I don’t need a totally different template for mobile, but just need to make a small change).

Vijay,
Thanks for posting a comment, and glad you have an opinion, but I have to argue that particular code is less efficient and actually does not take the solution all the way like mine does. Let me explain why:

1. minidetector loops through an array of strings (each string a mobile browser name) and it checks to see if the string is in the HTTP_USER_AGENT. This is performed on every incoming request. YIKES!! In my take on it, I compile a regex of the browser versions once on app load and reuse the compiled regex for each request. Using regex is much faster, and performance is important for something that is going to be run on every request to your site. Also, minibrowser only looks for browser names, and not versions numbers, which my regex does as well

2. minibrowser adds a mobile flag to each request like mine does, but what then? This is actually the reason i wrote the post, I wanted a way to use different templates when that flag is set, without having to throw an IF statement in every view. Using a template loader along with the middleware is the full solution and so that’s why I felt the need to write the post.

http://butenas.com Ignas

Worth to try Thanks for sharing.

http://alexdutton.co.uk/ Alexander Dutton

As an aside, if you’re after capabilities-based detection then using the wurfl database might be the way to go. A project I worked on had some middleware that would add a device attribute to the request¹. Matching is a bit slow, but cached indefinitely afterwards.

Hi !
Where should I put the mobile_templates folder?
Let me know.
Because I always get “Template does not exit” message.

Thanks
NN

http://www.mattotodd.com Sullerton

You should put it at the same level (sibling) as your existing “templates” folder.

Oluwaseun

Cool stuff. Its however difficult to decide where each snippet of code would be? If you could give a source code link to download it , that would be really great. Nice work dude. Saved me loads of time> And when I saw the minidetector and how much string it had to parse at every call, I thought to myself this would have some optimization hit. SO I guess what you did with precompiling the regex is a great one. Nice work.

Thanks for the middleware! I had used the same site’s nginx regex, but this is much simpler for deployment.

I did do one thing differently. Instead of writing my own loader, I just wrote a function in my app’s views.py http://dpaste.com/581582/

This had a couple advantages for me:
1. I could change the mobile dir name with just a setting.
2. The method of path construction accounts for subdirectories – useful if your templates directory has subdirectories.
3. If there is not a mobile template for the request, the app simply renders to the non-mobile template.

End result looks something like this:
return render_to_response(getRequestTemplate(‘main/foo.html’), context)

Thanks again!

http://www.mattotodd.com Sullerton

Thanks for the input Grant. I know mine is def unpolished.

Arshavski Alexander

Thank you a lot. I searched for a solution for 2 days and it’s just what I needed. Thanks again.

Ben D

That’s really cool. Works so easily and quickly. I’m still new to django but had it up and running in no time.

yeago

why not just set a cookie? that way the user could easily break out of the mobile version.

Yeah right. The minidetector is both slower and the features aren’t as nice. Unless you like slower code (looping through a list instead of a basic regular expression match), there is no way you’d prefer it over this solution. Unless of course you are Vijay posting under a fake name, trying to get people to pay attention to your less-capable project…

Idel

Good post. Look also my open source project called apache mobile filter. The main features are device detection, image rendering and mobile switcher. The mag supports several device repository such as wurfl, detectright, 51degrees.
For more info: http://apachemobilefilter.org