Mobile Detection on Django

Most of the Django sites I develop these days need to work on mobile. I am using Bootstrap 3 to make my pages responsive. Bootstrap’s philosophy is “mobile first”. But there are many cases where I do not want users with big screens to be penalized by a site that is optimized for a small screen.

While Bootstrap has some css classes for showing and hiding based on screen size. That approach is somewhat limited. It also slows the page down because the content still needs to be downloaded even if its not shown. A more complete solution would be to have the option of detecting the device on the server side and altering the page before its sent. This is especially easy using Django templates.

The big question is how to detect the users device and hopefully get its screen size in Django. I started with fiftyonedegrees.mobi. I was able to install it on Ubuntu 12.04 without any problems. I was using the “lite-pattern-wrapper” method, so I did not download the trie database. It worked fine, locally with the Django development server. However, when I tried to install it on a CentOS 5, 32-bit machine (Webfaction), it seg faulted. The folks at Webfaction suggested that I migrate to one of their CentOS 6, 64-bit machines. It installed without problems on the machine. But when I tried using the code by fetching a page from my DroidX, it seg faulted. I am not sure how to debug something like that. It works locally. And when it seg faults there are no debugging messages.

Wait. All is not lost. The middleware and context processor works with Firefox on Ubuntu. I added a bunch of print statements to the middleware. The problem occurs in the call to mobile_detector.match in _match. Up until then all the parameters look reasonable. To make matter worse, if I fire up my virtualenv and python interpreter on the server and run that command from the command line, with the params that caused the seg fault, everything works fine. It looks like this is a problem with mod_wsgi.

There is some stuff on stackoverflow related to this. I tried adding WSGIApplicationGroup %{GLOBAL} but no change. This is a mess. Moving on. I am going to try a pure python package.

django-mobile looks OK, but seems to be limited to just differentiating between mobile, tablet and full. Moble ESP looks interesting. The python code is just one file: mdetect.py. It’s very readable and understandable. Not as many Django features as django-mobile. But most of what I want, I can roll my own. Here is quick and dirty middleware:

 
from xxx import mdetect

class DetectMobile:
    def process_request(self, request):
        user_agent = request.META.get("HTTP_USER_AGENT")
        http_accept = request.META.get("HTTP_ACCEPT")
        if user_agent and http_accept:
            agent = mdetect.UAgentInfo(userAgent=user_agent, httpAccept=http_accept)
            request.mobile_esp_agent = agent   # in case we want more information about the device
            if agent.detectMobileQuick():
                request.device_type = 'mobile'
            elif agent.detectTierTablet():
                request.device_type = 'tablet'
            else:
                request.device_type = 'desktop'
        else:
            request.mobile_esp_agent = None
            request.device_type = 'desktop'   # default

The careful reader will no doubt notice that after I called django-mobile limited, I set about creating those exact limits in my middleware. Ha. But an even carefuller reader will notice I include the agent in request, so I can get at the details as needed. So far this approach is working well.