Browse Source

Fixed #19879 -- Have 'findstatic' says on which directories it searched the relative paths.

Added searched_locations in finders module. Added verbosity flag level 2 on 'findstatic'
command that will output the directories on which it searched the relative paths.

Reported by ccurvey. Initial patch by Jonas Svensson and Vajrasky Kok.
Vajrasky Kok 11 years ago
parent
commit
6a9ed7d403

+ 12 - 0
django/contrib/staticfiles/finders.py

@@ -12,11 +12,15 @@ from django.utils import six, lru_cache
 
 from django.contrib.staticfiles import utils
 
+# To keep track on which directories the finder has searched the static files.
+searched_locations = []
+
 
 class BaseFinder(object):
     """
     A base file finder to be used for custom staticfiles finder classes.
     """
+
     def find(self, path, all=False):
         """
         Given a relative file path this ought to find an
@@ -75,6 +79,8 @@ class FileSystemFinder(BaseFinder):
         """
         matches = []
         for prefix, root in self.locations:
+            if root not in searched_locations:
+                searched_locations.append(root)
             matched_path = self.find_location(root, path, prefix)
             if matched_path:
                 if not all:
@@ -147,6 +153,9 @@ class AppDirectoriesFinder(BaseFinder):
         """
         matches = []
         for app in self.apps:
+            app_location = self.storages[app].location
+            if app_location not in searched_locations:
+                searched_locations.append(app_location)
             match = self.find_in_app(app, path)
             if match:
                 if not all:
@@ -195,6 +204,8 @@ class BaseStorageFinder(BaseFinder):
         except NotImplementedError:
             pass
         else:
+            if self.storage.location not in searched_locations:
+                searched_locations.append(self.storage.location)
             if self.storage.exists(path):
                 match = self.storage.path(path)
                 if all:
@@ -232,6 +243,7 @@ def find(path, all=False):
     If ``all`` is ``False`` (default), return the first matching
     absolute path (or ``None`` if no match). Otherwise return a list.
     """
+    searched_locations[:] = []
     matches = []
     for finder in get_finders():
         result = finder.find(path, all=all)

+ 13 - 3
django/contrib/staticfiles/management/commands/findstatic.py

@@ -21,15 +21,25 @@ class Command(LabelCommand):
         verbosity = int(options.get('verbosity', 1))
         result = finders.find(path, all=options['all'])
         path = force_text(path)
+        if verbosity >= 2:
+            searched_locations = ("Looking in the following locations:\n  %s" %
+                                  "\n  ".join(force_text(location)
+                                  for location in finders.searched_locations))
+        else:
+            searched_locations = ''
         if result:
             if not isinstance(result, (list, tuple)):
                 result = [result]
             result = (force_text(os.path.realpath(path)) for path in result)
             if verbosity >= 1:
-                output = '\n  '.join(result)
-                return "Found '%s' here:\n  %s" % (path, output)
+                file_list = '\n  '.join(result)
+                return ("Found '%s' here:\n  %s\n%s" %
+                        (path, file_list, searched_locations))
             else:
                 return '\n'.join(result)
         else:
+            message = ["No matching file found for '%s'." % path]
+            if verbosity >= 2:
+                message.append(searched_locations)
             if verbosity >= 1:
-                self.stderr.write("No matching file found for '%s'." % path)
+                self.stderr.write('\n'.join(message))

+ 34 - 0
docs/ref/contrib/staticfiles.txt

@@ -162,6 +162,23 @@ output and just get the path names::
    /home/special.polls.com/core/static/css/base.css
    /home/polls.com/core/static/css/base.css
 
+On the other hand, by setting the :djadminopt:`--verbosity` flag to 2, you can
+get all the directories on which it searched the relative paths::
+
+   $ python manage.py findstatic css/base.css --verbosity 2
+   Found 'css/base.css' here:
+     /home/special.polls.com/core/static/css/base.css
+     /home/polls.com/core/static/css/base.css
+   Looking in the following locations:
+     /home/special.polls.com/core/static
+     /home/polls.com/core/static
+     /some/other/path/static
+
+.. versionadded:: 1.7
+
+   The additional message of on which directories it searched the relative
+   paths is new in Django 1.7.
+
 .. _staticfiles-runserver:
 
 runserver
@@ -366,6 +383,23 @@ files:
 
 .. _staticfiles-development-view:
 
+Finders Module
+==============
+
+``staticfiles`` finders has a ``searched_locations`` list with directory paths
+in which they search for the relative paths. Example usage::
+
+    from django.contrib.staticfiles import finders
+
+    result = finders.find('css/base.css')
+    searched_locations = finders.searched_locations
+
+.. versionadded:: 1.7
+
+The ``get_searched_locations`` function is new in Django 1.7. Previously, we
+have to check the locations of our :setting:`STATICFILES_FINDERS` manually
+one by one.
+
 Static file development view
 ----------------------------
 

+ 4 - 0
docs/releases/1.7.txt

@@ -1051,6 +1051,10 @@ Miscellaneous
   which does allow primary keys with value 0. It only forbids *autoincrement*
   primary keys with value 0.
 
+* :djadmin:`findstatic` now accepts verbosity flag level 2, meaning it will
+  show the directories on which it searched the relative paths. See
+  :djadmin:`findstatic` for example output.
+
 .. _deprecated-features-1.7:
 
 Features deprecated in 1.7

+ 37 - 0
tests/staticfiles_tests/tests.py

@@ -223,6 +223,35 @@ class TestFindStatic(CollectionTestCase, TestDefaults):
         self.assertIn('project', force_text(lines[0]))
         self.assertIn('apps', force_text(lines[1]))
 
+    def test_all_files_more_verbose(self):
+        """
+        Test that findstatic returns all candidate files if run without --first and -v2.
+        Also, test that findstatic returns the searched locations with -v2.
+        """
+        out = six.StringIO()
+        call_command('findstatic', 'test/file.txt', verbosity=2, stdout=out)
+        out.seek(0)
+        lines = [l.strip() for l in out.readlines()]
+        self.assertIn('project', force_text(lines[1]))
+        self.assertIn('apps', force_text(lines[2]))
+        self.assertIn("Looking in the following locations:", force_text(lines[3]))
+        searched_locations = ', '.join(lines[4:])
+        #AppDirectoriesFinder searched locations
+        self.assertIn(os.path.join('staticfiles_tests', 'apps', 'test', 'static'),
+                      searched_locations)
+        self.assertIn(os.path.join('staticfiles_tests', 'apps', 'no_label', 'static'),
+                      searched_locations)
+        self.assertIn(os.path.join('django', 'contrib', 'admin', 'static'),
+                      searched_locations)
+        self.assertIn(os.path.join('django', 'tests', 'servers', 'another_app', 'static'),
+                      searched_locations)
+        #FileSystemFinder searched locations
+        self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][1][1], searched_locations)
+        self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][0], searched_locations)
+        #DefaultStorageFinder searched locations
+        self.assertIn(os.path.join('staticfiles_tests', 'project', 'site_media', 'media'),
+                      searched_locations)
+
 
 class TestConfiguration(StaticFilesTestCase):
     def test_location_empty(self):
@@ -779,6 +808,9 @@ class TestDefaultStorageFinder(StaticFilesTestCase, FinderTestCase):
         self.find_all = ('media-file.txt', [test_file_path])
 
 
+@override_settings(STATICFILES_FINDERS=
+                   ('django.contrib.staticfiles.finders.FileSystemFinder',),
+                   STATICFILES_DIRS=[os.path.join(TEST_ROOT, 'project', 'documents')])
 class TestMiscFinder(TestCase):
     """
     A few misc finder tests.
@@ -805,6 +837,11 @@ class TestMiscFinder(TestCase):
         self.assertEqual(cache_info.hits, 9)
         self.assertEqual(cache_info.currsize, 1)
 
+    def test_searched_locations(self):
+        finders.find('spam')
+        self.assertEqual(finders.searched_locations,
+                         [os.path.join(TEST_ROOT, 'project', 'documents')])
+
     @override_settings(STATICFILES_DIRS='a string')
     def test_non_tuple_raises_exception(self):
         """