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.

staticfiles.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. from __future__ import absolute_import, unicode_literals
  2. from collections import OrderedDict
  3. from os.path import join, normpath
  4. from django.conf import settings
  5. from django.contrib.staticfiles import finders, storage
  6. from django.contrib.staticfiles.templatetags import staticfiles
  7. from django.core.files.storage import get_storage_class
  8. from django.utils.encoding import python_2_unicode_compatible
  9. from django.utils.functional import LazyObject
  10. from django.utils.translation import ugettext_lazy as _, ungettext
  11. from debug_toolbar import panels
  12. from debug_toolbar.utils import ThreadCollector
  13. try:
  14. import threading
  15. except ImportError:
  16. threading = None
  17. @python_2_unicode_compatible
  18. class StaticFile(object):
  19. """
  20. Representing the different properties of a static file.
  21. """
  22. def __init__(self, path):
  23. self.path = path
  24. def __str__(self):
  25. return self.path
  26. def real_path(self):
  27. return finders.find(self.path)
  28. def url(self):
  29. return storage.staticfiles_storage.url(self.path)
  30. class FileCollector(ThreadCollector):
  31. def collect(self, path, thread=None):
  32. # handle the case of {% static "admin/" %}
  33. if path.endswith('/'):
  34. return
  35. super(FileCollector, self).collect(StaticFile(path), thread)
  36. collector = FileCollector()
  37. class DebugConfiguredStorage(LazyObject):
  38. """
  39. A staticfiles storage class to be used for collecting which paths
  40. are resolved by using the {% static %} template tag (which uses the
  41. `url` method).
  42. """
  43. def _setup(self):
  44. configured_storage_cls = get_storage_class(settings.STATICFILES_STORAGE)
  45. class DebugStaticFilesStorage(configured_storage_cls):
  46. def __init__(self, collector, *args, **kwargs):
  47. super(DebugStaticFilesStorage, self).__init__(*args, **kwargs)
  48. self.collector = collector
  49. def url(self, path):
  50. self.collector.collect(path)
  51. return super(DebugStaticFilesStorage, self).url(path)
  52. self._wrapped = DebugStaticFilesStorage(collector)
  53. _original_storage = storage.staticfiles_storage
  54. class StaticFilesPanel(panels.Panel):
  55. """
  56. A panel to display the found staticfiles.
  57. """
  58. name = 'Static files'
  59. template = 'debug_toolbar/panels/staticfiles.html'
  60. @property
  61. def title(self):
  62. return (_("Static files (%(num_found)s found, %(num_used)s used)") %
  63. {'num_found': self.num_found, 'num_used': self.num_used})
  64. def __init__(self, *args, **kwargs):
  65. super(StaticFilesPanel, self).__init__(*args, **kwargs)
  66. self.num_found = 0
  67. self._paths = {}
  68. def enable_instrumentation(self):
  69. storage.staticfiles_storage = staticfiles.staticfiles_storage = DebugConfiguredStorage()
  70. def disable_instrumentation(self):
  71. storage.staticfiles_storage = staticfiles.staticfiles_storage = _original_storage
  72. @property
  73. def num_used(self):
  74. return len(self._paths[threading.currentThread()])
  75. nav_title = _('Static files')
  76. @property
  77. def nav_subtitle(self):
  78. num_used = self.num_used
  79. return ungettext("%(num_used)s file used",
  80. "%(num_used)s files used",
  81. num_used) % {'num_used': num_used}
  82. def process_request(self, request):
  83. collector.clear_collection()
  84. def generate_stats(self, request, response):
  85. used_paths = collector.get_collection()
  86. self._paths[threading.currentThread()] = used_paths
  87. self.record_stats({
  88. 'num_found': self.num_found,
  89. 'num_used': self.num_used,
  90. 'staticfiles': used_paths,
  91. 'staticfiles_apps': self.get_staticfiles_apps(),
  92. 'staticfiles_dirs': self.get_staticfiles_dirs(),
  93. 'staticfiles_finders': self.get_staticfiles_finders(),
  94. })
  95. def get_staticfiles_finders(self):
  96. """
  97. Returns a sorted mapping between the finder path and the list
  98. of relative and file system paths which that finder was able
  99. to find.
  100. """
  101. finders_mapping = OrderedDict()
  102. for finder in finders.get_finders():
  103. for path, finder_storage in finder.list([]):
  104. if getattr(finder_storage, 'prefix', None):
  105. prefixed_path = join(finder_storage.prefix, path)
  106. else:
  107. prefixed_path = path
  108. finder_cls = finder.__class__
  109. finder_path = '.'.join([finder_cls.__module__,
  110. finder_cls.__name__])
  111. real_path = finder_storage.path(path)
  112. payload = (prefixed_path, real_path)
  113. finders_mapping.setdefault(finder_path, []).append(payload)
  114. self.num_found += 1
  115. return finders_mapping
  116. def get_staticfiles_dirs(self):
  117. """
  118. Returns a list of paths to inspect for additional static files
  119. """
  120. dirs = []
  121. for finder in finders.get_finders():
  122. if isinstance(finder, finders.FileSystemFinder):
  123. dirs.extend(finder.locations)
  124. return [(prefix, normpath(dir)) for prefix, dir in dirs]
  125. def get_staticfiles_apps(self):
  126. """
  127. Returns a list of app paths that have a static directory
  128. """
  129. apps = []
  130. for finder in finders.get_finders():
  131. if isinstance(finder, finders.AppDirectoriesFinder):
  132. for app in finder.apps:
  133. if app not in apps:
  134. apps.append(app)
  135. return apps