1
from collections import OrderedDict
4
from django.apps import apps
2
5
from django.conf import settings
3
6
from django.core.exceptions import ImproperlyConfigured
4
7
from django.core.files.storage import default_storage, Storage, FileSystemStorage
5
from django.utils.datastructures import SortedDict
6
from django.utils.functional import empty, memoize, LazyObject
7
from django.utils.module_loading import import_by_path
8
from django.utils.functional import empty, LazyObject
9
from django.utils.module_loading import import_string
8
10
from django.utils._os import safe_join
9
from django.utils import six
11
from django.utils import six, lru_cache
11
13
from django.contrib.staticfiles import utils
12
from django.contrib.staticfiles.storage import AppStaticStorage
14
_finders = SortedDict()
15
# To keep track on which directories the finder has searched the static files.
16
searched_locations = []
17
19
class BaseFinder(object):
19
21
A base file finder to be used for custom staticfiles finder classes.
21
24
def find(self, path, all=False):
23
26
Given a relative file path this ought to find an
27
30
the first found file path will be returned; if set
28
31
to ``True`` a list of all found files paths is returned.
30
raise NotImplementedError()
33
raise NotImplementedError('subclasses of BaseFinder must provide a find() method')
32
35
def list(self, ignore_patterns):
35
38
a two item iterable consisting of the relative path and storage
38
raise NotImplementedError()
41
raise NotImplementedError('subclasses of BaseFinder must provide a list() method')
41
44
class FileSystemFinder(BaseFinder):
43
46
A static files finder that uses the ``STATICFILES_DIRS`` setting
46
def __init__(self, apps=None, *args, **kwargs):
49
def __init__(self, app_names=None, *args, **kwargs):
47
50
# List of locations with static files
48
51
self.locations = []
49
52
# Maps dir paths to an appropriate storage instance
50
self.storages = SortedDict()
53
self.storages = OrderedDict()
51
54
if not isinstance(settings.STATICFILES_DIRS, (list, tuple)):
52
55
raise ImproperlyConfigured(
53
56
"Your STATICFILES_DIRS setting is not a tuple or list; "
78
81
for prefix, root in self.locations:
82
if root not in searched_locations:
83
searched_locations.append(root)
79
84
matched_path = self.find_location(root, path, prefix)
110
115
class AppDirectoriesFinder(BaseFinder):
112
117
A static files finder that looks in the directory of each app as
113
specified in the source_dir attribute of the given storage class.
118
specified in the source_dir attribute.
115
storage_class = AppStaticStorage
120
storage_class = FileSystemStorage
121
source_dir = 'static'
117
def __init__(self, apps=None, *args, **kwargs):
123
def __init__(self, app_names=None, *args, **kwargs):
118
124
# The list of apps that are handled
120
# Mapping of app module paths to storage instances
121
self.storages = SortedDict()
123
apps = settings.INSTALLED_APPS
125
app_storage = self.storage_class(app)
126
# Mapping of app names to storage instances
127
self.storages = OrderedDict()
128
app_configs = apps.get_app_configs()
130
app_names = set(app_names)
131
app_configs = [ac for ac in app_configs if ac.name in app_names]
132
for app_config in app_configs:
133
app_storage = self.storage_class(
134
os.path.join(app_config.path, self.source_dir))
126
135
if os.path.isdir(app_storage.location):
127
self.storages[app] = app_storage
128
if app not in self.apps:
129
self.apps.append(app)
136
self.storages[app_config.name] = app_storage
137
if app_config.name not in self.apps:
138
self.apps.append(app_config.name)
130
139
super(AppDirectoriesFinder, self).__init__(*args, **kwargs)
132
141
def list(self, ignore_patterns):
146
155
for app in self.apps:
156
app_location = self.storages[app].location
157
if app_location not in searched_locations:
158
searched_locations.append(app_location)
147
159
match = self.find_in_app(app, path)
158
170
storage = self.storages.get(app, None)
161
prefix = '%s%s' % (storage.prefix, os.sep)
162
if not path.startswith(prefix):
164
path = path[len(prefix):]
165
172
# only try to find a file if the source dir actually exists
166
173
if storage.exists(path):
167
174
matched_path = storage.path(path)
197
204
except NotImplementedError:
207
if self.storage.location not in searched_locations:
208
searched_locations.append(self.storage.location)
200
209
if self.storage.exists(path):
201
210
match = self.storage.path(path)
234
243
If ``all`` is ``False`` (default), return the first matching
235
244
absolute path (or ``None`` if no match). Otherwise return a list.
246
searched_locations[:] = []
238
248
for finder in get_finders():
239
249
result = finder.find(path, all=all)
253
263
yield get_finder(finder_path)
256
def _get_finder(import_path):
266
@lru_cache.lru_cache(maxsize=None)
267
def get_finder(import_path):
258
269
Imports the staticfiles finder class described by import_path, where
259
270
import_path is the full Python path to the class.
261
Finder = import_by_path(import_path)
272
Finder = import_string(import_path)
262
273
if not issubclass(Finder, BaseFinder):
263
274
raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' %
264
275
(Finder, BaseFinder))
266
get_finder = memoize(_get_finder, _finders, 1)