如何通过package_name.module_name.class_name“path”实例化类

时间:2012-10-22 15:04:27

标签: python django django-fixtures

我想实现类似于django fixture系统的东西,在夹具中设置model属性,表示夹具的模型类。它看起来像这样

my_app.models.my_model

我的问题是处理这样一个字符串的标准方法是什么,以便创建这个“路径”所指向的类的实例。我认为应该看起来像:

  1. 将其拆分为模块名称和类名称部件
  2. 加载模块(如果未加载)
  3. 从模块中获取类名称
  4. 实例化
  5. 我该怎么做?

    编辑:我提出了一个肮脏的解决方案:

    def _resolve_class(self, class_path):
        tokens = class_path.split('.')
        class_name = tokens[-1]
        module_name = '.'.join(tokens[:-1])
        exec "from %s import %s" % (module_name, class_name)
        class_obj = locals()[class_name]
        return class_obj
    
    然而,由于exec的使用以及通过恶意准备灯具来操纵执行的可能性,它的工作却很脏。该如何正确完成?

4 个答案:

答案 0 :(得分:2)

我经常使用django的import_module函数,因为它在框架周围用来完成这个。

from django.utils.importlib import import_module

path = 'foo.bar.classname'
module_path, class_name = path.rsplit('.', 1)
module = import_module(module_path)
cls = getattr(module, class_name)()  # instantiated!

对于解决方案中的exec,您可以使用模块上的__import__轻松删除它,然后获取课程。

答案 1 :(得分:1)

答案 2 :(得分:1)

请注意,在函数中使用exec的危险在于它经常允许攻击者提供虚假值,这会导致您的函数“意外”执行攻击者想要的任何代码。在这里你直接编写一个允许这样的功能!使用exec并没有让它变得更糟。唯一的区别是没有exec他们必须弄清楚如何将他们的代码放到python的导入路径上的文件中。

这并不意味着你不应该这样做。只要知道你在做什么。插件框架固有地存在这个问题;使框架在运行时可扩展的全部意义在于,您希望能够配置插件的任何人能够在程序中执行他们喜欢的任何代码。如果您的程序将在最终用户与配置插件的人员不同的环境中使用,请确保以与_resolve_class相同的方式处理exec;不允许用户输入您直接传递给_resolve_class的字符串!

现在,除此之外,您可以非常轻松地避免使用exec。 Python有一个内置函数__import__,用于获取导入机制的底层实现。您可以使用它来进行动态导入(help(__import__)足以让我弄清楚如何编写这个答案;如果您需要更多细节,还有docs。使用它,您的功能可能类似于:

def _resolve_class(self, class_path):
    modulepath, classname = class_path.rsplit('.', 1)
    module = __import__(modulepath, fromlist=[classname])
    return getattr(module, classname)

(请注意,我还使用了rsplit最大拆分数,以避免必须拆分模块路径才重新加入它。

答案 3 :(得分:0)

只需复制Django json序列化程序使用的格式并编写对象创建代码。它定义了一个dicts列表。每个dict包含许多元数据字段,包括model,其中包含应用程序名称和模型名称,以及包含模型字段数据的嵌套字典:

[
    {
        "pk": 1,
        "model": "app_name.model_name",
        "fields": {
            "default_value": "",
            "category": null,
            "position": 10,
            ...
        }
    },
    {
        "pk": 2,
        "model": "app_name.model_name",
        "fields": {
            ...
        }
    }
]

所以使用json.loads()获取数据,然后遍历dicts并提取数据

for obj_dict in json.loads(data):
    app_name, model_name = obj_dict.split('.')
    # some magic here to import your model
    my_model = magic_import_function(app_name, model_name)
    foo = my_model(*obj_dict["fields"])
    foo.save()