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
This commit is contained in:
Jannis Leidel 2011-01-15 23:38:00 +00:00
parent 6c361ecb17
commit 645eb2b26b
6 changed files with 75 additions and 96 deletions

View File

@ -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):
"""

View File

@ -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']

View File

@ -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__))
# 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':
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_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'.
"""
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
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

View File

@ -3,32 +3,35 @@ import fnmatch
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
def is_ignored(path, ignore_patterns=[]):
"""
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
def get_files(storage, ignore_patterns=[], location=''):
"""
Recursively walk the storage directories gathering a complete
list of files that should be copied, returning this list.
Recursively walk the storage directories yielding the paths
of all files that should be copied.
"""
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
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():
"""

View File

@ -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

View File

@ -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