项目需求
最近在做一个Django项目,需要实现可以自己上传代码,来实现某些功能;类似于插件形式,项目的功能是由我上传的代码来实现。但是不影响项目的运行。
解决方案
这里有两个解决方案,
1、可执行文件
就是把要上传的代码封装成可执行文件,就是可以在命令行直接执行的文件,上传到项目中,这样就可以在已经运行的项目中直接执行代码。
缺点:就是传递参数只能通过cmd或者配置文件。也可直连数据库,这样就比较麻烦了。
2、动态加载代码
这个就是今天要说的,动态加载代码,可以直接把上传的代码文件(模块和类)加载到项目中直接调用执行。
动态加载
技术方案
1、项目中代码的目录结构
├─Item # 项目,主目录
│ ├─compose # 动态加载功能
│ │ ├─initiator.py # 动态加载代码,调用上传程序
│ │ ├─plugins # 上传代码的目录
│ │ │ ├─add_name.py # 上传的代码,用来动态加载
│ │ │ └─add_name2.py # 上传的代码,用来动态加载
2、代码
initiator.py 中代码如下:
class LazyImport(object):
"""
动态导入模块
"""
def __init__(self, module_name, module_class):
"""
:param module_name:
:param module_class:
:return: 等同于 form module_name import module_class
"""
self.module_name = module_name
self.module_class = module_class
self.module = None
def __getattr__(self, name):
if self.module is None:
self.module = __import__(self.module_name, fromlist=[self.module_class])
return getattr(self.module, name)
def test(filename, clsname):
importmodule = LazyImport("compose.plugins." + filename, clsname) # 导入classname模块
is_true = hasattr(importmodule, clsname) # 检查classname类是否在classname模块中
if is_true:
classname = getattr(importmodule, clsname)
# 传递参数
obj = classname('大花', '猪') # 实例化classname类
mtd = getattr(obj, 'run')
dd = mtd() # 调用run方法,接收return返回值
print(dd)
add_name.py 代码
class add_name(object):
def __init__(self, name, genre):
self.name = name
self.genre = genre
def run(self):
print("名称:{},类型:{}".format(self.name, self.genre))
return [self.name, self.genre]
add_name2.py 代码
class add_name2(object):
def __init__(self, name, genre):
self.name = name
self.genre = genre
def run(self):
print("你叫{},你是一个{}".format(self.name, self.genre))
return [self.genre, self.name]
3、执行动态加载
在Django项目运行中,可以直接上传代码到plugins目录中,然后调用initiator.py中的test()函数即可。
1)调用add_name
test("add_name", "add_name")
结果:
2)调用add_name2
test("add_name2", "add_name2")
结果: