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的引用。javascript
屬性self._js = [], self._css = {} 分別負責js和css的文件引用。css
方法add_js,add_css負責添加文件引用。html
方法render_js, render_css負責輸出引用js和css的html語句。java
經過對add_js(self, data)方法的分析, 能夠看出參數data的格式是['js_path_1', 'js_path_2']。self._js的格式也是由js文件名組成的列表。python
經過對add_css(self, data)方法的分析, 能夠看參數data的格式爲app
{'medium_type_1': ['type_1_path_1', 'type_1_path_2',....], url
'medium_type_2': ['type_2_path_1', ...]code
....}。orm
self._css的格式也是由medium:paths組成的字典。paths是由path組成的列表。htm
經過調用方法的形式,
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)})