Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

base.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import logging
  2. import types
  3. from django.conf import settings
  4. from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed
  5. from django.db import connections, transaction
  6. from django.urls import get_resolver, set_urlconf
  7. from django.utils.log import log_response
  8. from django.utils.module_loading import import_string
  9. from .exception import convert_exception_to_response, get_exception_response
  10. logger = logging.getLogger('django.request')
  11. class BaseHandler:
  12. _view_middleware = None
  13. _template_response_middleware = None
  14. _exception_middleware = None
  15. _middleware_chain = None
  16. def load_middleware(self):
  17. """
  18. Populate middleware lists from settings.MIDDLEWARE.
  19. Must be called after the environment is fixed (see __call__ in subclasses).
  20. """
  21. self._view_middleware = []
  22. self._template_response_middleware = []
  23. self._exception_middleware = []
  24. handler = convert_exception_to_response(self._get_response)
  25. for middleware_path in reversed(settings.MIDDLEWARE):
  26. middleware = import_string(middleware_path)
  27. try:
  28. mw_instance = middleware(handler)
  29. except MiddlewareNotUsed as exc:
  30. if settings.DEBUG:
  31. if str(exc):
  32. logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
  33. else:
  34. logger.debug('MiddlewareNotUsed: %r', middleware_path)
  35. continue
  36. if mw_instance is None:
  37. raise ImproperlyConfigured(
  38. 'Middleware factory %s returned None.' % middleware_path
  39. )
  40. if hasattr(mw_instance, 'process_view'):
  41. self._view_middleware.insert(0, mw_instance.process_view)
  42. if hasattr(mw_instance, 'process_template_response'):
  43. self._template_response_middleware.append(mw_instance.process_template_response)
  44. if hasattr(mw_instance, 'process_exception'):
  45. self._exception_middleware.append(mw_instance.process_exception)
  46. handler = convert_exception_to_response(mw_instance)
  47. # We only assign to this when initialization is complete as it is used
  48. # as a flag for initialization being complete.
  49. self._middleware_chain = handler
  50. def make_view_atomic(self, view):
  51. non_atomic_requests = getattr(view, '_non_atomic_requests', set())
  52. for db in connections.all():
  53. if db.settings_dict['ATOMIC_REQUESTS'] and db.alias not in non_atomic_requests:
  54. view = transaction.atomic(using=db.alias)(view)
  55. return view
  56. def get_exception_response(self, request, resolver, status_code, exception):
  57. return get_exception_response(request, resolver, status_code, exception, self.__class__)
  58. def get_response(self, request):
  59. """Return an HttpResponse object for the given HttpRequest."""
  60. # Setup default url resolver for this thread
  61. set_urlconf(settings.ROOT_URLCONF)
  62. response = self._middleware_chain(request)
  63. response._closable_objects.append(request)
  64. # If the exception handler returns a TemplateResponse that has not
  65. # been rendered, force it to be rendered.
  66. if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
  67. response = response.render()
  68. if response.status_code >= 400:
  69. log_response(
  70. '%s: %s', response.reason_phrase, request.path,
  71. response=response,
  72. request=request,
  73. )
  74. return response
  75. def _get_response(self, request):
  76. """
  77. Resolve and call the view, then apply view, exception, and
  78. template_response middleware. This method is everything that happens
  79. inside the request/response middleware.
  80. """
  81. response = None
  82. if hasattr(request, 'urlconf'):
  83. urlconf = request.urlconf
  84. set_urlconf(urlconf)
  85. resolver = get_resolver(urlconf)
  86. else:
  87. resolver = get_resolver()
  88. resolver_match = resolver.resolve(request.path_info)
  89. callback, callback_args, callback_kwargs = resolver_match
  90. request.resolver_match = resolver_match
  91. # Apply view middleware
  92. for middleware_method in self._view_middleware:
  93. response = middleware_method(request, callback, callback_args, callback_kwargs)
  94. if response:
  95. break
  96. if response is None:
  97. wrapped_callback = self.make_view_atomic(callback)
  98. try:
  99. response = wrapped_callback(request, *callback_args, **callback_kwargs)
  100. except Exception as e:
  101. response = self.process_exception_by_middleware(e, request)
  102. # Complain if the view returned None (a common error).
  103. if response is None:
  104. if isinstance(callback, types.FunctionType): # FBV
  105. view_name = callback.__name__
  106. else: # CBV
  107. view_name = callback.__class__.__name__ + '.__call__'
  108. raise ValueError(
  109. "The view %s.%s didn't return an HttpResponse object. It "
  110. "returned None instead." % (callback.__module__, view_name)
  111. )
  112. # If the response supports deferred rendering, apply template
  113. # response middleware and then render the response
  114. elif hasattr(response, 'render') and callable(response.render):
  115. for middleware_method in self._template_response_middleware:
  116. response = middleware_method(request, response)
  117. # Complain if the template response middleware returned None (a common error).
  118. if response is None:
  119. raise ValueError(
  120. "%s.process_template_response didn't return an "
  121. "HttpResponse object. It returned None instead."
  122. % (middleware_method.__self__.__class__.__name__)
  123. )
  124. try:
  125. response = response.render()
  126. except Exception as e:
  127. response = self.process_exception_by_middleware(e, request)
  128. return response
  129. def process_exception_by_middleware(self, exception, request):
  130. """
  131. Pass the exception to the exception middleware. If no middleware
  132. return a response for this exception, raise it.
  133. """
  134. for middleware_method in self._exception_middleware:
  135. response = middleware_method(request, exception)
  136. if response:
  137. return response
  138. raise