Wednesday, March 11, 2009

Getting client OS in Django

Some times it can be useful to serve our site content with little differences depending on the visitor operating system. I really think it's a bad idea changing the content or doing some big changes, depending on it, but this post can be useful for it as well.

So, while most time just some Javascript is used to customize user experience based on its operating system, few times it'll also be useful to do it in the server side.

For those cases, here you've a simple context processor that will make available a template variable named "platform" which content can be "Linux", "Mac" or "Windows".


import re

def user_agent(request):
'''
Context processor for Django that provides operating system
information base on HTTP user agent.
A user agent looks like (line break added):
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.6) \
Gecko/2009020409 Iceweasel/3.0.6 (Debian-3.0.6-1)"
'''
print 'user_agent'
# Mozilla/5.0
regex = '(?P<application_name>\w+)/(?P<application_version>[\d\.]+)'
regex += ' \('
# X11
regex += '(?P<compatibility_flag>\w+)'
regex += '; '
# U
regex += '(?P<version_token>[\w .]+)'
regex += '; '
# Linux i686
regex += '(?P<platform_token>[\w .]+)'
# anything else
regex += '; .*'

user_agent = request.META['HTTP_USER_AGENT']
result = re.match(regex, user_agent)
if result:
result_dict = result.groupdict()
full_platform = result_dict['platform_token']
platform_values = full_platform.split(' ')
if platform_values[0] in ('Windows', 'Linux', 'Mac'):
platform = platform_values[0]
elif platform_values[1] in ('Mac',):
# Mac is given as "PPC Mac" or "Intel Mac"
platform = platform_values[1]
else:
platform = None
else:
full_platform = None
platform = None

return {
'user-agent': user_agent,
'full_platform': full_platform,
'platform': platform,
}


To make it work just copy the code in a file

myproject/myapp/context_processors.py

add it to context processors in settings

TEMPLATE_CONTEXT_PROCESSORS = ('myproject.myapp.context_processors.user_agent', [...])

and don't forget to add the RequestContext parameter if you are processing your template with render_to_response and want the variable available

from django.template import RequestContext
[...]
render_to_response('mytemplate.html', mycontext, RequestContext(request))


Then you'll be able to do something like that in your templates:

<p>You are a {{ platform }} user.</p>

4 comments:

  1. Looks like the group names in your regex disappeared. Need to escape the < and >
    :)

    ReplyDelete
  2. You're right.

    Now it's already fixed.

    Thank you so much for pointing this out.

    ReplyDelete
  3. Marc, I have updated the Transdb project to support the the 1.0 branch of Django, and I've made a number of improvements to the admin interface. I have screenshots and source code I would like to send you, but I don't have your email address. Please let me know your email address, and I'll send you the files.

    Thanks!
    -Jonathan
    Jonathan.Oomph at gmail dot com

    ReplyDelete
  4. The snippet does not seem to work with the http user agent coming from Opera browser.

    ReplyDelete