BaseFinder是所有Finder的基类:
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。
子类FileSystemFinder则实现了settings.STATICFILES_DIRS的寻找。
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的格式为
[(prefix, dir), (prefix, dir), ...]
self.storages的格式为
{dir: FileSystemStorage, dir: FileSystemStorage, ...}
find方法负责寻找文件的位置:
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的文件位置。
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负责展示文件列表:
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判断的。