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.

middleware.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. """
  2. Debug Toolbar middleware
  3. """
  4. from __future__ import absolute_import, unicode_literals
  5. import re
  6. import threading
  7. from django.conf import settings
  8. from django.utils import six
  9. from django.utils.deprecation import MiddlewareMixin
  10. from django.utils.encoding import force_text
  11. from django.utils.lru_cache import lru_cache
  12. from django.utils.module_loading import import_string
  13. from debug_toolbar import settings as dt_settings
  14. from debug_toolbar.toolbar import DebugToolbar
  15. _HTML_TYPES = ('text/html', 'application/xhtml+xml')
  16. def show_toolbar(request):
  17. """
  18. Default function to determine whether to show the toolbar on a given page.
  19. """
  20. if request.META.get('REMOTE_ADDR', None) not in settings.INTERNAL_IPS:
  21. return False
  22. return bool(settings.DEBUG)
  23. @lru_cache()
  24. def get_show_toolbar():
  25. # If SHOW_TOOLBAR_CALLBACK is a string, which is the recommended
  26. # setup, resolve it to the corresponding callable.
  27. func_or_path = dt_settings.get_config()['SHOW_TOOLBAR_CALLBACK']
  28. if isinstance(func_or_path, six.string_types):
  29. return import_string(func_or_path)
  30. else:
  31. return func_or_path
  32. class DebugToolbarMiddleware(MiddlewareMixin):
  33. """
  34. Middleware to set up Debug Toolbar on incoming request and render toolbar
  35. on outgoing response.
  36. """
  37. debug_toolbars = {}
  38. def process_request(self, request):
  39. # Decide whether the toolbar is active for this request.
  40. show_toolbar = get_show_toolbar()
  41. if not show_toolbar(request):
  42. return
  43. # Don't render the toolbar during AJAX requests.
  44. if request.is_ajax():
  45. return
  46. toolbar = DebugToolbar(request)
  47. self.__class__.debug_toolbars[threading.current_thread().ident] = toolbar
  48. # Activate instrumentation ie. monkey-patch.
  49. for panel in toolbar.enabled_panels:
  50. panel.enable_instrumentation()
  51. # Run process_request methods of panels like Django middleware.
  52. response = None
  53. for panel in toolbar.enabled_panels:
  54. response = panel.process_request(request)
  55. if response:
  56. break
  57. return response
  58. def process_view(self, request, view_func, view_args, view_kwargs):
  59. toolbar = self.__class__.debug_toolbars.get(threading.current_thread().ident)
  60. if not toolbar:
  61. return
  62. # Run process_view methods of panels like Django middleware.
  63. response = None
  64. for panel in toolbar.enabled_panels:
  65. response = panel.process_view(request, view_func, view_args, view_kwargs)
  66. if response:
  67. break
  68. return response
  69. def process_response(self, request, response):
  70. toolbar = self.__class__.debug_toolbars.pop(threading.current_thread().ident, None)
  71. if not toolbar:
  72. return response
  73. # Run process_response methods of panels like Django middleware.
  74. for panel in reversed(toolbar.enabled_panels):
  75. new_response = panel.process_response(request, response)
  76. if new_response:
  77. response = new_response
  78. # Deactivate instrumentation ie. monkey-unpatch. This must run
  79. # regardless of the response. Keep 'return' clauses below.
  80. # (NB: Django's model for middleware doesn't guarantee anything.)
  81. for panel in reversed(toolbar.enabled_panels):
  82. panel.disable_instrumentation()
  83. # Check for responses where the toolbar can't be inserted.
  84. content_encoding = response.get('Content-Encoding', '')
  85. content_type = response.get('Content-Type', '').split(';')[0]
  86. if any((getattr(response, 'streaming', False),
  87. 'gzip' in content_encoding,
  88. content_type not in _HTML_TYPES)):
  89. return response
  90. # Collapse the toolbar by default if SHOW_COLLAPSED is set.
  91. if toolbar.config['SHOW_COLLAPSED'] and 'djdt' not in request.COOKIES:
  92. response.set_cookie('djdt', 'hide', 864000)
  93. # Insert the toolbar in the response.
  94. content = force_text(response.content, encoding=response.charset)
  95. insert_before = dt_settings.get_config()['INSERT_BEFORE']
  96. pattern = re.escape(insert_before)
  97. bits = re.split(pattern, content, flags=re.IGNORECASE)
  98. if len(bits) > 1:
  99. # When the toolbar will be inserted for sure, generate the stats.
  100. for panel in reversed(toolbar.enabled_panels):
  101. panel.generate_stats(request, response)
  102. panel.generate_server_timing(request, response)
  103. response = self.generate_server_timing_header(response, toolbar.enabled_panels)
  104. bits[-2] += toolbar.render_toolbar()
  105. response.content = insert_before.join(bits)
  106. if response.get('Content-Length', None):
  107. response['Content-Length'] = len(response.content)
  108. return response
  109. @staticmethod
  110. def generate_server_timing_header(response, panels):
  111. data = []
  112. for panel in panels:
  113. stats = panel.get_server_timing_stats()
  114. if not stats:
  115. continue
  116. for key, record in stats.items():
  117. # example: `SQLPanel_sql_time=0; "SQL 0 queries"`
  118. data.append('{}_{}={}; "{}"'.format(panel.panel_id,
  119. key,
  120. record.get('value'),
  121. record.get('title')))
  122. if data:
  123. response['Server-Timing'] = ', '.join(data)
  124. return response