Writing this essay is because today in the process of writing plug-ins and filtering methods of the wall, tossing for a long time finally found some problems, write down in order to make a memo.
After looking at the plug-in mechanism of xadmin, I also want to use this idea to extend the method of view in Kadmin.
For example, in a login view, the general logic is as follows:
classLoginView (baseadminview):" "Login View" "Auth_form=none#form Classes for authenticationLogin_template=None title="" defUpdate_login_params (self,defaults):" "hooks for modifying parameters before executing the login view function" "return defaults@never_cachedefGet (self,request,*arg,**Kwargs): fromDjango.contrib.auth.viewsImportLogin Context=self.get_context (Request,*arg,**kwargs)#Gets the context of the parent classcontext.update ({'title': Self.title,'App_path': Request.get_full_path, REDIRECT_FIELD_NAME:request.get_full_path (),}) Defaults={ 'Extra_context': Context,'Authentication_form': Self.auth_formorAdminauthenticationform,'Template_name': Self.login_templateor 'kadmin/views/login.html', }
defaults=
return login (request,**def Post (self,request,*arg,** return self.get (Request,*arg,**kwargs)
We want to be able to update the context before executing the login function through the Update_login_params method, and the plugin should be used to take over the execution of the method to make some modifications as needed.
Here are two ways of thinking:
1, filter the results of Update_login_params and return
2, the use of plug-in functions, control the execution of Update_login_params (generally live update_login_params execution results The incoming plug-in function is modified by the plugin function and then returned)
You can also use the plug-in function to take over the update_login_params and decide for yourself whether to execute update_login_params or perform some behavior before or after Update_login_params execution.
From the above two points, the plug-in is more flexible and free, the control range is larger, the second filter can only operate on the return value of Update_login_params and return.
1. Filter
Define Filter Adorner @filter_hook
defFilter_hook (func):#Filter Adorner #used to re-process the returned value of the hook methodFunc_name=func.__name__@wraps (func)defInner_func (self,*arg,**Kwargs):ifGetAttr (Self,'Filters', None): Filter_infos=[(GetAttr (FI,' Priority', (), FI) forFiinchSelf.filtersifFi.__name__==Func_name] Filters=[fi forP,fiinchSorted (filter_infos,key=LambdaX:x[0],reverse=False)] Result=func (self,*arg,**Kwargs) forFiinchFilters:result=fi (self,result,*arg,**Kwargs)returnresultElse: returnFunc (self,*arg,**Kwargs)returnInner_func
Source code Description: Traverse filters on the View object self to get the filter function, filter the result and return the result
For example, we can define a filter to add parentheses to the return value of Say_hello.
@set_attr (attr_name='__name__', value='say_hello') def add_tag (self,result,o,*arg,**Kwargs): return"(%s) "%result
classPerson:name='Akun'Sex="Akun-male"Plugins=[maleplugin (Sex_num) forSex_numinchRange (4)] Filters=[Add_tag] @filter_hook @plugin_hookdefSay_hello (self,go,*arg,**Kwargs):Print("-----------Done--------") return "Hello I am%s"%Self.nameif __name__=="__main__": P=Person ()Print(P.say_hello ('Go'))
Description: @set_attr is an adorner that is responsible for setting the property value of the function, since Filter_hook is looking for a function with the same name as the hook when looking for the filter function, so Add_tag __name__ must be set to Say_hello
2. Plugins
To define a plug-in adorner:
defPlugin_chain (funcs,token,func,*arg,**Kwargs):ifToken==-1: returnfunc () @wraps (func)def_inner_func (): Wrap_func=Funcs[token] Arg_specs=Getargspec (Wrap_func) [0]ifLen (arg_specs) ==1: func ()returnWrap_func (*arg,**Kwargs)elifLen (arg_specs) >=2 andarg_specs[1]=="__": Back=funcElse: Back=func ()returnWrap_func (back,*arg,**Kwargs)returnPlugin_chain (funcs,token-1,_inner_func,*arg,**Kwargs)defPlugin_hook (func): Func_name=func.__name__@wraps (func)defMethod (self,*arg,**Kwargs): @wraps (func)def_inner_func ():returnFunc (self,*arg,**Kwargs)ifGetAttr (Self,'Plugins', None): Plugin_funcs=[(GetAttr (P,func_name), GetAttr (GetAttr (P,func_name),' Priority', 10)) forPinchSelf.pluginsifGetAttr (p,func_name,none)]Print(Plugin_funcs)#to add a to the plug-in method in ascending orderPlugin_funcs=[p forP,priorityinchSorted (plugin_funcs,key=LambdaX:x[1],reverse=False)] returnPlugin_chain (Plugin_funcs,len (Plugin_funcs) -1,_inner_func,*arg,**Kwargs)Else: returnFunc (self,*arg,**Kwargs)returnMethod
Source code Description: Similar to @filter_hook just, note
wrap_func=funcs[token] arg_specs =ge Targspec (Wrap_func) [0] if len (arg_specs) ==1: Func () return wrap_func (*arg,**kwargs) elif len (arg_specs) >=2 and< /span> arg_specs[1]== " : Back =func else : Back =func ()
return
Wrap_func (Back,*arg,**kwargs)
If the parameter named "__" is used to receive the return value of the hook method, __ will be set as the method of the hook, that is, the method of the hook can be taken over by the plugin.
We define a plugin to print out hooks before executing say_hello.
class Beforehook: @set_attr (attr_name='priority', value=6) def Say_hello (self,__,o,*arg,**Kwargs): print("Theback is%s "% __"return __ (*arg,**kwargs)
Here __ or is set as the method of being hook
Inspiration-python-django in the development of plugins and filter decorators