소스 검색

Stopped staticfiles app from requiring a models module when looking for static files. Also removed a bit of code smell in the prefix handling by saving it in the source file storage.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15219 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Jannis Leidel 14 년 전
부모
커밋
645eb2b26b

+ 33 - 28
django/contrib/staticfiles/finders.py

@@ -55,7 +55,9 @@ class FileSystemFinder(BaseFinder):
             self.locations.add((prefix, root))
         # Don't initialize multiple storages for the same location
         for prefix, root in self.locations:
-            self.storages[root] = FileSystemStorage(location=root)
+            filesystem_storage = FileSystemStorage(location=root)
+            filesystem_storage.prefix = prefix
+            self.storages[root] = filesystem_storage
 
         super(FileSystemFinder, self).__init__(*args, **kwargs)
 
@@ -94,7 +96,7 @@ class FileSystemFinder(BaseFinder):
         for prefix, root in self.locations:
             storage = self.storages[root]
             for path in utils.get_files(storage, ignore_patterns):
-                yield path, prefix, storage
+                yield path, storage
 
 
 class AppDirectoriesFinder(BaseFinder):
@@ -105,14 +107,18 @@ class AppDirectoriesFinder(BaseFinder):
     storage_class = AppStaticStorage
 
     def __init__(self, apps=None, *args, **kwargs):
-        # Maps app modules to appropriate storage instances
+        # The list of apps that are handled
+        self.apps = []
+        # Mapping of app module paths to storage instances
         self.storages = SortedDict()
-        if apps is not None:
-            self.apps = apps
-        else:
-            self.apps = models.get_apps()
-        for app in self.apps:
-            self.storages[app] = self.storage_class(app)
+        if apps is None:
+            apps = settings.INSTALLED_APPS
+        for app in apps:
+            app_storage = self.storage_class(app)
+            if os.path.isdir(app_storage.location):
+                self.storages[app] = app_storage
+                if app not in self.apps:
+                    self.apps.append(app)
         super(AppDirectoriesFinder, self).__init__(*args, **kwargs)
 
     def list(self, ignore_patterns):
@@ -121,9 +127,8 @@ class AppDirectoriesFinder(BaseFinder):
         """
         for storage in self.storages.itervalues():
             if storage.exists(''): # check if storage location exists
-                prefix = storage.get_prefix()
                 for path in utils.get_files(storage, ignore_patterns):
-                    yield path, prefix, storage
+                    yield path, storage
 
     def find(self, path, all=False):
         """
@@ -131,29 +136,29 @@ class AppDirectoriesFinder(BaseFinder):
         """
         matches = []
         for app in self.apps:
-            app_matches = self.find_in_app(app, path)
-            if app_matches:
+            match = self.find_in_app(app, path)
+            if match:
                 if not all:
-                    return app_matches
-                matches.append(app_matches)
+                    return match
+                matches.append(match)
         return matches
 
     def find_in_app(self, app, path):
         """
         Find a requested static file in an app's static locations.
         """
-        storage = self.storages[app]
-        prefix = storage.get_prefix()
-        if prefix:
-            prefix = '%s%s' % (prefix, os.sep)
-            if not path.startswith(prefix):
-                return None
-            path = path[len(prefix):]
-        # only try to find a file if the source dir actually exists
-        if storage.exists(path):
-            matched_path = storage.path(path)
-            if matched_path:
-                return matched_path
+        storage = self.storages.get(app, None)
+        if storage:
+            if storage.prefix:
+                prefix = '%s%s' % (storage.prefix, os.sep)
+                if not path.startswith(prefix):
+                    return None
+                path = path[len(prefix):]
+            # only try to find a file if the source dir actually exists
+            if storage.exists(path):
+                matched_path = storage.path(path)
+                if matched_path:
+                    return matched_path
 
 
 class BaseStorageFinder(BaseFinder):
@@ -196,7 +201,7 @@ class BaseStorageFinder(BaseFinder):
         List all files of the storage.
         """
         for path in utils.get_files(self.storage, ignore_patterns):
-            yield path, '', self.storage
+            yield path, self.storage
 
 class DefaultStorageFinder(BaseStorageFinder):
     """

+ 5 - 5
django/contrib/staticfiles/management/commands/collectstatic.py

@@ -75,8 +75,8 @@ Type 'yes' to continue, or 'no' to cancel: """)
                 raise CommandError("Collecting static files cancelled.")
 
         for finder in finders.get_finders():
-            for source, prefix, storage in finder.list(ignore_patterns):
-                self.copy_file(source, prefix, storage, **options)
+            for source, storage in finder.list(ignore_patterns):
+                self.copy_file(source, storage, **options)
 
         actual_count = len(self.copied_files) + len(self.symlinked_files)
         unmodified_count = len(self.unmodified_files)
@@ -97,7 +97,7 @@ Type 'yes' to continue, or 'no' to cancel: """)
         if self.verbosity >= level:
             self.stdout.write(msg)
 
-    def copy_file(self, source, prefix, source_storage, **options):
+    def copy_file(self, source, source_storage, **options):
         """
         Attempt to copy (or symlink) ``source`` to ``destination``,
         returning True if successful.
@@ -107,8 +107,8 @@ Type 'yes' to continue, or 'no' to cancel: """)
             source_last_modified = source_storage.modified_time(source)
         except (OSError, NotImplementedError):
             source_last_modified = None
-        if prefix:
-            destination = os.path.join(prefix, source)
+        if getattr(source_storage, 'prefix', None):
+            destination = os.path.join(source_storage.prefix, source)
         else:
             destination = source
         symlink = options['link']

+ 11 - 39
django/contrib/staticfiles/storage.py

@@ -38,50 +38,22 @@ class AppStaticStorage(FileSystemStorage):
     A file system storage backend that takes an app module and works
     for the ``static`` directory of it.
     """
+    prefix = None
     source_dir = 'static'
 
     def __init__(self, app, *args, **kwargs):
         """
         Returns a static file storage if available in the given app.
         """
-        # app is actually the models module of the app. Remove the '.models'.
-        bits = app.__name__.split('.')[:-1]
-        self.app_name = bits[-1]
-        self.app_module = '.'.join(bits)
-        # The models module (app) may be a package in which case
-        # dirname(app.__file__) would be wrong. Import the actual app
-        # as opposed to the models module.
-        app = import_module(self.app_module)
-        location = self.get_location(os.path.dirname(app.__file__))
-        super(AppStaticStorage, self).__init__(location, *args, **kwargs)
-
-    def get_location(self, app_root):
-        """
-        Given the app root, return the location of the static files of an app,
-        by default 'static'. We special case the admin app here since it has
-        its static files in 'media'.
-        """
+        # app is the actual app module
+        self.app_module = app
+        # We special case the admin app here since it has its static files
+        # in 'media' for historic reasons.
         if self.app_module == 'django.contrib.admin':
-            return os.path.join(app_root, 'media')
-        return os.path.join(app_root, self.source_dir)
-
-    def get_prefix(self):
-        """
-        Return the path name that should be prepended to files for this app.
-        """
-        if self.app_module == 'django.contrib.admin':
-            return self.app_name
-        return None
+            self.prefix = 'admin'
+            self.source_dir = 'media'
+        mod = import_module(self.app_module)
+        mod_path = os.path.dirname(mod.__file__)
+        location = os.path.join(mod_path, self.source_dir)
+        super(AppStaticStorage, self).__init__(location, *args, **kwargs)
 
-    def get_files(self, ignore_patterns=[]):
-        """
-        Return a list containing the relative source paths for all files that
-        should be copied for an app.
-        """
-        files = []
-        prefix = self.get_prefix()
-        for path in utils.get_files(self, ignore_patterns):
-            if prefix:
-                path = os.path.join(prefix, path)
-            files.append(path)
-        return files

+ 21 - 18
django/contrib/staticfiles/utils.py

@@ -3,32 +3,35 @@ import fnmatch
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
 
-def get_files(storage, ignore_patterns=[], location=''):
+def is_ignored(path, ignore_patterns=[]):
     """
-    Recursively walk the storage directories gathering a complete
-    list of files that should be copied, returning this list.
+    Return True or False depending on whether the ``path`` should be
+    ignored (if it matches any pattern in ``ignore_patterns``).
     """
-    def is_ignored(path):
-        """
-        Return True or False depending on whether the ``path`` should be
-        ignored (if it matches any pattern in ``ignore_patterns``).
-        """
-        for pattern in ignore_patterns:
-            if fnmatch.fnmatchcase(path, pattern):
-                return True
-        return False
+    for pattern in ignore_patterns:
+        if fnmatch.fnmatchcase(path, pattern):
+            return True
+    return False
 
+def get_files(storage, ignore_patterns=[], location=''):
+    """
+    Recursively walk the storage directories yielding the paths
+    of all files that should be copied.
+    """
     directories, files = storage.listdir(location)
-    static_files = [location and os.path.join(location, fn) or fn
-                    for fn in files
-                    if not is_ignored(fn)]
+    for fn in files:
+        if is_ignored(fn, ignore_patterns):
+            continue
+        if location:
+            fn = os.path.join(location, fn)
+        yield fn
     for dir in directories:
-        if is_ignored(dir):
+        if is_ignored(dir, ignore_patterns):
             continue
         if location:
             dir = os.path.join(location, dir)
-        static_files.extend(get_files(storage, ignore_patterns, dir))
-    return static_files
+        for fn in get_files(storage, ignore_patterns, dir):
+            yield fn
 
 def check_settings():
     """

+ 2 - 3
docs/ref/contrib/staticfiles.txt

@@ -103,9 +103,8 @@ setting.
 
 .. note::
 
-    When using the :class:`AppDirectoriesFinder` finder, make sure your apps can
-    be found by Django's app loading mechanism. Simply include a ``models``
-    module (an empty ``models.py`` file suffices) and add the app to the
+    When using the :class:`AppDirectoriesFinder` finder, make sure your apps
+    can be found by staticfiles. Simply add the app to the
     :setting:`INSTALLED_APPS` setting of your site.
 
 Static file finders are currently considered a private interface, and this

+ 3 - 3
tests/regressiontests/staticfiles_tests/tests.py

@@ -34,9 +34,6 @@ class StaticFilesTestCase(TestCase):
         self.old_debug = settings.DEBUG
         self.old_installed_apps = settings.INSTALLED_APPS
 
-        # We have to load these apps to test staticfiles.
-        load_app('regressiontests.staticfiles_tests.apps.test')
-        load_app('regressiontests.staticfiles_tests.apps.no_label')
         site_media = os.path.join(TEST_ROOT, 'project', 'site_media')
         settings.DEBUG = True
         settings.MEDIA_ROOT =  os.path.join(site_media, 'media')
@@ -53,8 +50,11 @@ class StaticFilesTestCase(TestCase):
             'django.contrib.staticfiles.finders.DefaultStorageFinder',
         )
         settings.INSTALLED_APPS = [
+            'django.contrib.admin',
             'django.contrib.staticfiles',
             'regressiontests.staticfiles_tests',
+            'regressiontests.staticfiles_tests.apps.test',
+            'regressiontests.staticfiles_tests.apps.no_label',
         ]
 
         # Clear the cached default_storage out, this is because when it first