1

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

my_app.models.my_model

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

  1. 将其拆分为模块名称和类名称部分
  2. 加载模块(如果未加载)
  3. 按名称从模块中获取类
  4. 实例化它

我应该怎么做?

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

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

4 回答 4

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__,然后获取类。

于 2012-10-22T20:56:30.117 回答
1

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

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

现在,除此之外,您可以exec很容易地避免使用。__import__Python 有一个用于获取导入机制的底层实现的内置函数。您可以使用它进行动态导入(help(__import__)足以让我弄清楚编写此答案的工作原理;如果您需要更多详细信息,还可以参考文档)。使用它,您的函数可能类似于:

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

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

于 2012-10-22T23:15:48.540 回答
1

编辑:为什么不基本上做loaddata命令的作用? https://github.com/django/django/blob/master/django/core/management/commands/loaddata.py#L184 使用djangodeserialize函数: https ://docs.djangoproject.com/en/dev/topics/序列化/#deserializing-数据

于 2012-10-22T15:46:24.747 回答
0

just copy the format used by the Django json serializer and write object creation code. It defines a list of dicts. Each dict contains a number of meta-data fields including model which contains the app name and model name and also a nested dict containing the model field data:

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

so use json.loads() to get the data then iterate over the dicts and pull out the data

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()
于 2012-10-22T17:53:43.003 回答