xadmin的視圖方法中如果加了@filter_hook 標記的都可以作為插件的鉤子函數。
例如在ListAdminView類中有許多加了上述標記的方法,
@filter_hook def get_context(self): """ PRepare the context for templates. """ self.title = _('%s List') % force_unicode(self.opts.verbose_name) model_fields = [(f, f.name in self.list_display, self.get_check_field_url(f)) for f in (self.opts.fields + self.get_model_method_fields()) if f.name not in self.list_exclude] new_context = { 'module_name': force_unicode(self.opts.verbose_name_plural), 'title': self.title, 'cl': self, 'model_fields': model_fields, 'clean_select_field_url': self.get_query_string(remove=[COL_LIST_VAR]), 'has_add_permission': self.has_add_permission(), 'app_label': self.app_label, 'brand_name': self.opts.verbose_name_plural, 'brand_icon': self.get_model_icon(self.model), 'add_url': self.model_admin_url('add'), 'result_headers': self.result_headers(), 'results': self.results() } context = super(ListAdminView, self).get_context() context.update(new_context) return context @filter_hook def get_response(self, context, *args, **kwargs): pass在上述代碼中,get_context方法被作為了一個插件鉤子函數,當調用該方法的時候,會遍歷ListAdminView注冊的插件尋找插件中與get_context同名的方法,并把get_context
執行后的結果作為第二個參數(第一個參數是self)傳給插件的get_context方法,于是我們可以在get_context方法返回結果前對其結果進行一些修改。正是因為如此,插件的同名方法會比視圖的方法多一個參數(用于接收上一個方法傳來的返回值)。但這不是絕對的。如果被hook的方法沒有返回值則插件方法可以不用多設一個參數。
例如,我們想給ListAdminView傳給模板的context中增加一個變量(var),我們可以這樣定義一個插件:
以下是插件機制實現的原理,其實就是wrap 目標方法,在裝飾器中遍歷插件形成針對目標方法的插件方法列表,然后在目標方法執行后遞歸執行。
注意:比較巧妙的是,func if fargs[1] == '__' else func(),也就是說可以根據參數名來決定是把上一結果傳過來還是把上一方法傳過來,如果是把上一方法傳過來
則可以控制方法的執行順序,可以在上一方法執行前做點改動。
def filter_chain(filters, token, func, *args, **kwargs): if token == -1: return func() else: def _inner_method(): fm = filters[token] fargs = getargspec(fm)[0] if len(fargs) == 1: # Only self arg result = func() if result is None: return fm() else: raise IncorrectPluginArg(u'Plugin filter method need a arg to receive parent method result.') else: return fm(func if fargs[1] == '__' else func(), *args, **kwargs) return filter_chain(filters, token - 1, _inner_method, *args, **kwargs)def filter_hook(func): tag = func.__name__ func.__doc__ = "``filter_hook``/n/n" + (func.__doc__ or "") @functools.wraps(func) def method(self, *args, **kwargs): def _inner_method(): return func(self, *args, **kwargs) if self.plugins: filters = [(getattr(getattr(p, tag), 'priority', 10), getattr(p, tag)) for p in self.plugins if callable(getattr(p, tag, None))] filters = [f for p, f in sorted(filters, key=lambda x:x[0])] return filter_chain(filters, len(filters) - 1, _inner_method, *args, **kwargs) else: return _inner_method() return method
新聞熱點
疑難解答