MEDIA_TYPES = ('css', 'js') class Media(object): def __init__(self, media=None, **kwargs): if media: media_attrs = media.__dict__ else: media_attrs = kwargs self._css = {} self._js = [] for name in MEDIA_TYPES: getattr(self, 'add_' + name)(media_attrs.get(name, None)) def __str__(self): return self.render() def render(self): return mark_safe('\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))) def render_js(self): return [ format_html( '<script type="text/javascript" src="{0}"></script>', self.absolute_path(path) ) for path in self._js ] def render_css(self): # To keep rendering order consistent, we can't just iterate over items(). # We need to sort the keys, and iterate over the sorted list. media = sorted(self._css.keys()) return chain(*[[ format_html( '<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(path), medium ) for path in self._css[medium] ] for medium in media]) def absolute_path(self, path, prefix=None): if path.startswith(('http://', 'https://', '/')): return path if prefix is None: if settings.STATIC_URL is None: # backwards compatibility prefix = settings.MEDIA_URL else: prefix = settings.STATIC_URL return urljoin(prefix, path) def __getitem__(self, name): "Returns a Media object that only contains media of the given type" if name in MEDIA_TYPES: return Media(**{str(name): getattr(self, '_' + name)}) raise KeyError('Unknown media type "%s"' % name) def add_js(self, data): if data: for path in data: if path not in self._js: self._js.append(path) def add_css(self, data): if data: for medium, paths in data.items(): for path in paths: if not self._css.get(medium) or path not in self._css[medium]: self._css.setdefault(medium, []).append(path) def __add__(self, other): combined = Media() for name in MEDIA_TYPES: getattr(combined, 'add_' + name)(getattr(self, '_' + name, None)) getattr(combined, 'add_' + name)(getattr(other, '_' + name, None)) return combined
Media负责对js和css的引用。
属性self._js = [], self._css = {} 分别负责js和css的文件引用。
方法add_js,add_css负责添加文件引用。
方法render_js, render_css负责输出引用js和css的html语句。
通过对add_js(self, data)方法的分析, 可以看出参数data的格式是['js_path_1', 'js_path_2']。self._js的格式也是由js文件名组成的列表。
通过对add_css(self, data)方法的分析, 可以看参数data的格式为
{'medium_type_1': ['type_1_path_1', 'type_1_path_2',....],
'medium_type_2': ['type_2_path_1', ...]
....}。
self._css的格式也是由medium:paths组成的字典。paths是由path组成的列表。
通过调用方法的形式,
getattr(self, 'add_' + name), getattr(self, 'render_' + name)
可以看到都是由
MEDIA_TYPES = ('css', 'js')
来命名的。
我们可以看到render()和render_css()都使用了chain方法。
首先来看render_css(),
chain(*[[ format_html( '<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(path), medium ) for path in self._css[medium] ] for medium in media])
看嵌套列表表达式
[[ format_html( '<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(path), medium ) for path in self._css[medium] ] for medium in media]
这里返回的是一个嵌套的列表,格式为
[ [ '<link href="path_1" type="text/css" media="medium_type_1" rel="stylesheet" />', '<link href="path_2" type="text/css" media="medium_type_1" rel="stylesheet" />' ] #medium_type_1 [ '<link href="path_1" type="text/css" media="medium_type_2" rel="stylesheet" />', '<link href="path_2" type="text/css" media="medium_type_2" rel="stylesheet" />' ] #medium_type_2 ... ]
然后使用chain(*lists),使用*传参,是chain比较常用的技巧。
render()方法的chain调用,也是一样的
最后说下Media使用的流程。
初始化
def __init__(self, media=None, **kwargs):
优先使用media参数,其次使用kwargs参数。
还可以看出这些参数只有这些属性有用
MEDIA_TYPES = ('css', 'js')
并且这些格式必须符合add_js( )和add_css( )的参数data格式。
使用add_js( )和add_css( )添加文件路径。
使用render_js( )和 render_css( ), render( )输出html语句。
注意 absolute_path(self, path, prefix=None)方法,
对path有特殊的要求。
获取js和css
__getitem__(self, name):
使得我们可以通过字典的方式获得。它返回的是一个新的meida实例。
return Media(**{str(name): getattr(self, '_' + name)})