Source code for awesome_django_timezones.middleware

from ipaddress import ip_address
from urllib.parse import quote, unquote
from django.conf import settings
from django.utils import timezone
import pytz
import ipapi


[docs]class TimezonesMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): """ Attempts to activate a timezone from a cookie. Otherwise uses IP API to lookup timezone """ # Code to be executed for each request before # the view (and later middleware) are called. tz = None # Must be setup to use timezones in order for this code to be usable if getattr(settings, 'USE_TZ', None): # check the cookie for `timezone` tz = request.COOKIES.get('timezone', None) if tz: # cookie timezones are URI encoded tz = unquote(tz) else: # no cookie set, use IP API to lookup timezone and use that to set the cookie ip_info = ipapi.location(ip=get_ip_address(request), key=getattr(settings, 'DJANGO_IPAPI_KEY', None)) tz = ip_info.get('timezone', None) try: # attempt to activate the timezone - this might be an invalid timezone timezone.activate(pytz.timezone(tz)) except (pytz.UnknownTimeZoneError, AttributeError): timezone.activate(get_default_tz()) response = self.get_response(request) # Code to be executed for each request/response after # the view is called. if getattr(settings, 'USE_TZ', None) and tz: # set or re-set the cookie if `tz` is provided, this extends the cookie expiration if it's already # set, or will set it initially by the API lookup to prevent future API lookups response.set_cookie('timezone', quote(tz, safe=''), max_age=60*60*24*365, samesite='Strict') return response
[docs]def get_ip_address(request): """ Makes the best attempt to get the client's real IP or return the loopback """ POSSIBLE_HEADERS = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_FORWARDED' 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'REMOTE_ADDR' ] ip = '' for header in POSSIBLE_HEADERS: possible_ip = request.META.get(header) if possible_ip: if 'for' in possible_ip and '=' in possible_ip: # Using new-ish `FORWADED` header # https://en.wikipedia.org/wiki/X-Forwarded-For#Alternatives_and_variations possible_ip = possible_ip.split('=')[1] if ',' in possible_ip: # Assume first IP address in list is the originating IP address # https://en.wikipedia.org/wiki/X-Forwarded-For#Format possible_ip = possible_ip.split(',')[0] try: ip = ip_address(possible_ip) except ValueError: # IP address isn't valid, keep checking headers continue # Ensure IP address isn't private or otherwise reserved if ip.is_multicast or ip.is_private or ip.is_unspecified or\ ip.is_reserved or ip.is_loopback or ip.is_link_local: # IP address isn't valid, keep checking headers continue if ip: # IP address is valid this far, consider it valid break return ip
[docs]def get_default_tz(): if hasattr(settings, 'AWESOME_TZ_DEFAULT_TZ'): return settings.AWESOME_TZ_DEFAULT_TZ else: if hasattr(settings, 'TIME_ZONE'): tz = settings.TIME_ZONE else: tz = 'UTC' try: return pytz.timezone(tz) except (pytz.UnknownTimeZoneError, AttributeError): return pytz.utc