django-user_agents
Django middleware that parses browser, OS, and device info from every request's user agent string.
Repository Health
Technical Analysis
django-user-agents plugs the user-agents parsing library into Django’s request/response cycle, attaching a lazily-evaluated user_agent object to every incoming request. It exposes browser, operating system, and device family/version details, plus boolean flags for mobile, tablet, PC, bot, and touch-capable clients, without requiring you to touch the request pipeline yourself.
Beyond the middleware, the package ships a get_user_agent/get_and_set_user_agent utility pair for use outside request-scoped code, five ready-to-use template filters (is_mobile, is_tablet, is_pc, is_bot, is_touch_capable), and pluggable caching so repeated parsing of the same UA string, a comparatively expensive regex-driven ua-parser operation, is avoided in production.
What You Get
- UserAgentMiddleware that lazily attaches a parsed user_agent object to request via SimpleLazyObject
- get_user_agent()/get_and_set_user_agent() utility functions for use outside the request/response cycle
- Five Django template filters: is_mobile, is_tablet, is_pc, is_bot, is_touch_capable
- Configurable Django cache backend integration to avoid re-parsing identical UA strings
- MD5-keyed cache key generation that safely handles user agent strings longer than 250 characters
Common Use Cases
- Serving a mobile-optimized template variant based on request.user_agent.is_mobile
- Gating touch-friendly UI components with the is_touch_capable template filter
- Filtering bot traffic out of analytics or A/B test logic using is_bot
- Logging browser/OS breakdowns for support tickets without a client-side JS library
Under The Hood
Architecture
The package is a flat, three-file integration layer: middleware.py defines UserAgentMiddleware, which wraps request-scoped parsing in a SimpleLazyObject so get_user_agent() only runs when request.user_agent is actually accessed; utils.py holds get_user_agent()/get_and_set_user_agent() plus the MD5-based cache-key logic and Django cache-backend resolution driven by the USER_AGENTS_CACHE setting; templatetags/user_agents.py registers five filters that all funnel through get_and_set_user_agent(). All three surfaces converge on the same underlying call into the external user-agents library’s parse(), so the package itself carries no parsing logic or internal abstractions beyond this thin, single-responsibility wrapper.
Tech Stack
Pure Python, written to be Python 2/3 compatible (explicit sys.version_info branching for the text_type alias) and Django-version-tolerant (try/except imports bridging Django’s old get_cache() and current caches[] cache API, and legacy django.core.urlresolvers.reverse vs django.urls.reverse in tests). Its only runtime dependencies are Django itself and the user-agents library (which in turn wraps ua-parser’s regex database). Packaged with plain setuptools (setup.py), tested across a Django/Python matrix via tox.ini and .travis.yml, with no build step beyond that.
Code Quality
Tests use Django’s SimpleTestCase, RequestFactory, and Client, covering middleware attachment, cache set/hit behavior, disabled and custom cache backends, template filter loading and correctness, long UA strings requiring the MD5 cache-key path, and unicode UA string handling, a solid suite for the package’s small surface. There are no type hints (the code predates broad typing adoption and targets Python 2), no linter configuration is present, and error handling is minimal: the one explicit guard is a hasattr(request, 'META') check in get_user_agent(), with everything else relying on the underlying user-agents library’s own defaults.
API Design
The package optimizes hard for zero-boilerplate Django ergonomics: one middleware entry plus request.user_agent.is_mobile in a view, or {% load user_agents %} plus {{ request|is_mobile }} in a template, with no explicit parsing or caching code required from the consumer. It introduces no new capability over the underlying user-agents library; its entire value is idiomatic integration and transparent caching, prioritizing familiarity over novelty.