django-maintenancemode
django-maintenancemode copied to clipboard
Better customization
Hi @bashu
I'm thinking about using a settings object that reads from a dict in django settings. Something like https://github.com/encode/django-rest-framework/blob/master/rest_framework/settings.py
What do you think?
This is because we could try and provide more configuration details such as:
- return json instead of html, with custom message
- custom methods for bypassing the maintenance mode
- turning on/off the methods used in processing the request
This is our current setup:
import re
from abc import abstractmethod
from typing import Type
import django
from django.conf import settings, urls
from django.core.exceptions import MiddlewareNotUsed
from django.http import HttpRequest, JsonResponse
from django.urls import get_resolver, resolvers
from django.utils.deprecation import MiddlewareMixin
from feature_flags import FlagKeys, get_variation
# Borrowed heavily from https://github.com/shanx/django-maintenancemode
# TODO: push the changes here to the repo as contributions.
IGNORE_URLS = tuple(re.compile(u) for u in settings.MAINTENANCE_IGNORE_URLS)
class BaseExclusionHandler:
def __init__(self, request: HttpRequest):
self.request = request
@abstractmethod
def should_bypass(self) -> bool:
raise NotImplementedError
class LDExclusionHandler(BaseExclusionHandler):
def should_bypass(self) -> bool:
return get_variation(
FlagKeys.should_bypass_maintenance_mode,
user_obj=self.request.user,
default_behavior=False,
extra_attrs=dict(path=self.request.path_info),
)
def temporary_unavailable(request): # noqa
return HttpResponseTemporaryUnavailable(
dict(
message="We will be back soon.", # TODO: load message from FormSettings
)
)
# todo: move to package settings
response_class = JsonResponse # could also be HttpResponse
exclusion_handler: Type[BaseExclusionHandler] = LDExclusionHandler # todo: make this a list of handlers
return_view = temporary_unavailable
urls.handler503 = return_view
urls.__all__.append("handler503")
class HttpResponseTemporaryUnavailable(response_class):
status_code = 503
class MaintenanceModeMiddleware(MiddlewareMixin):
def __init__(self, get_response):
if not settings.MAINTENANCE_MODE:
raise MiddlewareNotUsed()
super().__init__(get_response=get_response)
@staticmethod
def process_request(request: HttpRequest):
# Allow access if middleware is not activated
print(request.path_info)
if exclusion_handler(request).should_bypass():
return None
# Check if a path is explicitly excluded from maintenance mode
for url in IGNORE_URLS:
if url.match(request.path_info):
return None
if django.__version__ < "3.2":
# Checks if DJANGO version is less than 3.2.0 for breaking change
resolver = get_resolver()
callback, param_dict = resolver.resolve_error_handler("503")
return callback(request, **param_dict)
else:
# Default behaviour for django 3.2 and higher
resolver = resolvers.get_resolver(None)
resolve = resolver.resolve_error_handler
callback = resolve("503")
return callback(request)