def attrs(request):
	import re
	version_finders = {
	    'safari': re.compile(r'version/([\d]+)\.'),
	    'chrome': re.compile(r'chrome/([\d]+\.[\d]+)'),
	    'firefox': re.compile(r'firefox/([\d]+\.[\d]+)'),
	    'ie': re.compile(r'msie ([\d]+)\.'),
	    'blackberry': re.compile(r'blackberry[^/]+/([\d]+\.[\d]+)'),
	}

    def is_blank(item):
        return item != ''

	body_classes = []

    path = filter(is_blank,request.path.split('/'))
    if len(path) == 0:
        body_id = 'home'
        body_classes = 'home'
    else:
        body_id = path[len(path) - 1]
        try:
            body_id = int(body_id)
            body_id = path[len(path) - 2].rstrip('s') + '-' + path[len(path) - 1]
        except:
            pass
        loop = 0
        for part in path:
            try:
                piece = int(part)
                part = u'%s-%d' % (path[loop - 1], piece)
            except:
                pass
            body_classes.append(part)
            loop += 1
            
    user_agent = ''
    is_iphone = False
    is_ipad = False
    is_android = False
    is_blackberry = False
    is_app = False
    if 'HTTP_USER_AGENT' in request.META:
        user_agent = request.META['HTTP_USER_AGENT'].lower()

    # browser type
    if user_agent.find('webkit') > -1:
        body_classes.append('webkit')
    if user_agent.find('safari/') > -1 and not user_agent.find('chrome/') > -1:
        body_classes.append('safari')
        if version_finders['safari'].search(user_agent) is not None:
            body_classes.append('safari-' + version_finders['safari'].search(user_agent).group(1))
    if user_agent.find('chrome/') > -1:
        body_classes.append('chrome')
        if version_finders['chrome'].search(user_agent) is not None:
            body_classes.append('chrome-' + version_finders['chrome'].search(user_agent).group(1).replace('.', '_'))
    if user_agent.find('firefox') > -1:
        body_classes.append('firefox')
        if version_finders['firefox'].search(user_agent) is not None:
            body_classes.append('firefox-' + version_finders['firefox'].search(user_agent).group(1).replace('.', '_'))
    if user_agent.find('msie') > -1:
        body_classes.append('ie')
        if version_finders['ie'].search(user_agent) is not None:
            body_classes.append('ie-' + version_finders['ie'].search(user_agent).group(1))
    
    # operating system
    if user_agent.find('macintosh') > -1:
        body_classes.append('os-x')
    if user_agent.find('windows') > -1:
        body_classes.append('windows')
    if user_agent.find('linux') > -1:
        body_classes.append('linux')
    if user_agent.find('android') > -1:
        body_classes.append('android')
        is_android = True
    if user_agent.find('like mac os x') > -1:
        body_classes.append('ios')
    if user_agent.find('ipad') > -1:
        body_classes.append('ipad')
        is_ipad = True
    if (user_agent.find('iphone') > -1) or (user_agent.find('ipod') > -1):
        body_classes.append('iphone')
        is_iphone = True
    if user_agent.find('blackberry') > -1:
        body_classes.append('blackberry')
        is_blackberry = True
        
    val = {
        'body': {
            'id': body_id,
            'classes': ' '.join(body_classes),
        },
        'mobile': {
            'iphone': is_iphone,
            'ipad': is_ipad,
            'android': is_android,
            'blackberry': is_blackberry,
            'any': (is_iphone or is_ipad or is_android or is_blackberry or is_app),
        },
    }
    return val

I use this on all my Django projects. It does a few things:

  • turn the request's URL structure into a set of classes for use on body
  • makes a unique-ish ID for body
  • adds more classes to identify the browser of the request
  • identifies the major mobile platforms

Usage

Add it to your context_processors.py file and then add it to settings.CONTEXT_PROCESSORS (obviously).

<body id="{{ body.id }}" class="{{ body.classes }}">

{% if mobile.any %}<p>Hi mobile user</p>{% endif %}

Now instead of needing to do several different stylesheets for different browsers, you can just do one and prefix a rule with the appropriate browser class.

#wrapper { background: rgb(255,255,255); }

.webkit #wrapper { background: rgba(255,255,255,0.75); }

.ie6 #wrapper { display: none !important; /*suckers */ }

It should go without saying that generally its better to target abilities than browsers, (see modernizr.com/ for some help there) but occasionally you have a display issue that's browser specific where this totally makes sense to do.