Skip to content

Instantly share code, notes, and snippets.

@fmalina
Created October 1, 2021 15:08
Show Gist options
  • Save fmalina/7ab2acfa426f287acb7bdb4784dda59c to your computer and use it in GitHub Desktop.
Save fmalina/7ab2acfa426f287acb7bdb4784dda59c to your computer and use it in GitHub Desktop.
Mobile phone detection middleware for Django
"""Mobile phone detection middleware for Django"""
from django.utils.cache import patch_vary_headers
class MobileMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def is_mobile(self, ua):
ua = ua.lower()
for s in ['ipad', 'tablet']:
if s in ua:
return 0
for s in ['phone', 'mobile', 'symbian', 'smartphone',
'midp', 'wap', 'pda', 'mini', 'palm',
'netfront', 'opera mobi', 'windows ce']:
if s in ua:
return 1
return 0
def __call__(self, request):
ua = request.META.get('HTTP_USER_AGENT', '')
mobile = device = self.is_mobile(ua)
prefer = request.session.get('mobile', '')
switch = request.GET.get('mobile', '')
if prefer in [0, 1] and prefer != mobile:
mobile = prefer
if switch in ['0', '1']:
mobile = int(switch)
request.device = device # Is this a mobile device?
request.mobile = mobile # Show mobile site?
request.session['mobile'] = mobile
response = self.get_response(request)
# patch_vary_headers(response, ['User-Agent'])
return response
#### Tests
from django.contrib.sessions.models import Session
from django.test import Client, TestCase
from unittest.mock import MagicMock, Mock, patch
from adaptive import MobileMiddleware
MOBI_VERSION_SIGNATURE = 'MOBILE = true'
FULL_VERSION_SIGNATURE = 'MOBILE = false'
class MobileMiddlewareTests(TestCase):
def test_mobile_browser_agent(self):
request = Mock()
request.session = {}
request.META = {'HTTP_USER_AGENT': 'My Mobile Browser'}
MobileMiddleware(request)
self.assertEqual(request.mobile, 1)
def test_desktop_browser_agent(self):
request = Mock()
request.session = {}
request.META = {'HTTP_USER_AGENT': 'My Desktop Browser'}
MobileMiddleware(request)
self.assertEqual(request.mobile, 0)
def test_set_default_version(self):
request = Mock()
request.session = {}
request.META = MagicMock()
request.GET = {}
MobileMiddleware(request)
self.assertEqual(request.mobile, 0)
def test_set_version_through_get_parameter(self):
request = Mock()
request.session = {}
request.META = MagicMock()
request.GET = {'mobile': '1'}
MobileMiddleware(request)
self.assertEqual(request.mobile, 1)
TEST_URL = '/cookies' # use a very barebones view
class RealAgentNameTests(TestCase):
def assertFull(self, agent):
client = Client(HTTP_USER_AGENT=agent)
response = client.get(TEST_URL)
if FULL_VERSION_SIGNATURE not in response.content.decode():
self.fail(f'Agent is matched as mobile: {agent}')
def assertMobile(self, agent):
client = Client(HTTP_USER_AGENT=agent)
response = client.get(TEST_URL)
if MOBI_VERSION_SIGNATURE not in response.content.decode():
self.fail(f'Agent is not matched as mobile: {agent}')
def test_ipad(self):
self.assertFull('Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10')
def test_iphone(self):
self.assertMobile('Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3')
def test_motorola_xoom(self):
self.assertFull('Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13')
def test_opera_mobile_on_android(self):
self.assertMobile('Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1111101157; U; en) Presto/2.9.201 Version/11.50')
class RegressionTests(TestCase):
def setUp(self):
self.desktop = Client()
self.mobile = Client(HTTP_USER_AGENT='mobile')
def test_multiple_browser_access(self):
response = self.desktop.get(TEST_URL)
self.assertContains(response, FULL_VERSION_SIGNATURE)
response = self.mobile.get(TEST_URL)
self.assertContains(response, MOBI_VERSION_SIGNATURE)
response = self.desktop.get(TEST_URL)
self.assertContains(response, FULL_VERSION_SIGNATURE)
response = self.mobile.get(TEST_URL)
self.assertContains(response, MOBI_VERSION_SIGNATURE)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment