BaseFinder是全部Finder的基類:python
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 absolute file path. If the ``all`` parameter is ``False`` (default) only the first found file path will be returned; if set to ``True`` a list of all found files paths is returned. """ raise NotImplementedError('subclasses of BaseFinder must provide a find() method') def list(self, ignore_patterns): """ Given an optional list of paths to ignore, this should return a two item iterable consisting of the relative path and storage instance. """ raise NotImplementedError('subclasses of BaseFinder must provide a list() method')
BaseFinder定義了兩個方法find和list。app
子類FileSystemFinder則實現了settings.STATICFILES_DIRS的尋找。ide
class FileSystemFinder(BaseFinder): """ A static files finder that uses the ``STATICFILES_DIRS`` setting to locate files. """ def __init__(self, app_names=None, *args, **kwargs): # List of locations with static files self.locations = [] # Maps dir paths to an appropriate storage instance self.storages = OrderedDict() if not isinstance(settings.STATICFILES_DIRS, (list, tuple)): raise ImproperlyConfigured( "Your STATICFILES_DIRS setting is not a tuple or list; " "perhaps you forgot a trailing comma?") for root in settings.STATICFILES_DIRS: if isinstance(root, (list, tuple)): prefix, root = root else: prefix = '' if settings.STATIC_ROOT and os.path.abspath(settings.STATIC_ROOT) == os.path.abspath(root): raise ImproperlyConfigured( "The STATICFILES_DIRS setting should " "not contain the STATIC_ROOT setting") if (prefix, root) not in self.locations: self.locations.append((prefix, root)) for prefix, root in self.locations: filesystem_storage = FileSystemStorage(location=root) filesystem_storage.prefix = prefix self.storages[root] = filesystem_storage super(FileSystemFinder, self).__init__(*args, **kwargs)
FileSystemFinder類的初始化方法__init__,主要是初始化了self.locations和self.storages兩個屬性。函數
self.locations的格式爲 this
[(prefix, dir), (prefix, dir), ...]
self.storages的格式爲spa
{dir: FileSystemStorage, dir: FileSystemStorage, ...}
find方法負責尋找文件的位置:code
find只是循環遍歷self.locations,而後調用find_location方法在每一個dir尋找。對象
def find(self, path, all=False): """ Looks for files in the extra locations as defined in ``STATICFILES_DIRS``. """ 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: return matched_path matches.append(matched_path) return matches
find_location負責在給定的root目錄下, 尋找文件名爲path的文件位置。ci
def find_location(self, root, path, prefix=None): """ Finds a requested static file in a location, returning the found absolute path (or ``None`` if no match). """ if prefix: prefix = '%s%s' % (prefix, os.sep) if not path.startswith(prefix): return None path = path[len(prefix):] path = safe_join(root, path) if os.path.exists(path): return path
list負責展現文件列表:get
def list(self, ignore_patterns): """ List all files in all locations. """ for prefix, root in self.locations: storage = self.storages[root] for path in utils.get_files(storage, ignore_patterns): yield path, storage
調用get_files獲取path列表。
def get_files(storage, ignore_patterns=None, location=''): """ Recursively walk the storage directories yielding the paths of all files that should be copied. """ if ignore_patterns is None: ignore_patterns = [] directories, files = storage.listdir(location) for fn in files: if matches_patterns(fn, ignore_patterns): continue if location: fn = os.path.join(location, fn) yield fn for dir in directories: if matches_patterns(dir, ignore_patterns): continue if location: dir = os.path.join(location, dir) for fn in get_files(storage, ignore_patterns, dir): yield fn
這裏顯示利用storage對象,獲得文件列表,而後將結果經過ignore_patterns過濾,返回過濾後的文件。
AppDirectoriesFinder負責每一個app下面的static文件的查找。
class AppDirectoriesFinder(BaseFinder): """ A static files finder that looks in the directory of each app as specified in the source_dir attribute. """ storage_class = FileSystemStorage source_dir = 'static' def __init__(self, app_names=None, *args, **kwargs): # The list of apps that are handled self.apps = [] # Mapping of app names to storage instances self.storages = OrderedDict() app_configs = apps.get_app_configs() if app_names: app_names = set(app_names) app_configs = [ac for ac in app_configs if ac.name in app_names] for app_config in app_configs: app_storage = self.storage_class(os.path.join(app_config.path, self.source_dir)) if os.path.isdir(app_storage.location): self.storages[app_config.name] = app_storage if app_config.name not in self.apps: self.apps.append(app_config.name) super(AppDirectoriesFinder, self).__init__(*args, **kwargs)
self.apps的格式爲:
[app_name, app_name, ...]
self.storages的格式爲:
{app_name: FileSystemStorage, app_name: FileSystemStorage, ...}
調用apps模塊的get_app_configs函數,獲取app_config。
而後更新self.apps和self.storages
def find(self, path, all=False): """ Looks for files in the app directories. """ 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: return match matches.append(match) return matches
find方法循環遍歷self.apps,調用find_in_app方法指定一個app中尋找。
def find_in_app(self, app, path): """ Find a requested static file in an app's static locations. """ storage = self.storages.get(app, None) if storage: # 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的exists方法,來判斷是否存在。
def list(self, ignore_patterns): """ List all files in all app storages. """ for storage in six.itervalues(self.storages): if storage.exists(''): # check if storage location exists for path in utils.get_files(storage, ignore_patterns): yield path, storage
FileSystemFinder和AppDirectoriesFinder都也使用了FileSystemStorage,對於每個目錄,都有對應的storage。
不一樣的是FileSystemFinder經過os.path.exist判斷文件是否存在,而AppDirectoriesFinder是經過storage.exist判斷的。