【问题标题】:Is there a way to dynamically create django models(classes)?有没有办法动态创建 django 模型(类)?
【发布时间】:2021-10-08 21:12:18
【问题描述】:



我最近被分配了一项具有挑战性的任务,我们正在为我们的客户设计一个网站,我的老板希望我创建灵活的模型创建功能:这意味着他不希望我们对模型进行“硬编码”,他告诉我们不要手动定义 django 模型,而是在系统运行时创建它们。

例如,我们最初没有与业务流程相关的模型,管理员用户可以在我们的首页定义表单的格式,稍后将由普通用户处理。然后我们的后端接收来自前端的请求并使用提供的字段创建表单模型。我发现我可以通过 type() 函数创建一个类,比如 type(class_name, (models.Model,), attrs),并且我确实通过这个函数获得了新类。

但我想知道如何在我们的系统中保留这个类?就像将这个新类保存到我的 models.py 中并进行迁移,以便我们的数据库也拥有它

为了简化这一点,我的伪代码如下:

form_class_attrs = {
    'test_val': models.FloatField(),
    'test_name': models.CharField(),
    '__module__': __name__,
    }

def test(request):
    form_list = ['a', 'b']
    for form in form_list:
        form_class_name = "form_%s" % form
        attrs = form_class_attrs.copy()
        form_class = type(form_class_name, (models.Model,), attrs)

这里我在for循环中创建了两个类,那么如何在for循环中持久化每个form_class并进行相应的迁移呢?

【问题讨论】:

  • 请发布您尝试过的内容,以便人们帮助理解并帮助您实现您想要的。
  • 您可能想深入了解github.com/rvinzent/django-dynamic-models的源代码
  • @AMG 谢谢!我正在研究这个,这似乎是我需要的。

标签: python django oop design-patterns


【解决方案1】:

在调用test 之后,您的动态创建的类应该等效于等效的声明性模型 - 这是正确的。

为了让 django 使用这些模型,必须将它们传递给它们才能被使用。因此,您创建了一个 form_class 变量,它是 test 工厂函数的本地变量:您必须将此值返回到函数外部,并保留对创建的每个类的进程范围引用,以便 Django 可以使用它。 (每次需要时调用函数并重新创建模型是行不通的,因为 Django 会创建一个与前一个模型冲突的新模型,至少在所需的 ORM 资源中)。

除此之外,您不应该只为所有类创建一次字段实例,并尝试在它们上调用form_class_attrs.copy:这会尝试在不同的类中重用相同的字段。沿着这条线,您将不得不使用deepcopy- 但也有一个巨大的机会也不起作用,因为在创建字段时会生成内部标识符。您必须重新创建字段实例(即为每个动态类实际调用 .CharField()。最简单的方法(不是最好的)就是将 attr_fields 声明移到 for 循环内。

调用工厂函数一次,并返回列表或字典中的所有模型就足以满足模型的使用 - 但不适用于迁移目的(包括创建类) - Django 中的迁移机制可能依赖于导入所有模块并检查它们现有模型:我不确定这一点,但我认为这取决于在模块命名空间中找到模型,而不仅仅是“创建”。因此,必须在导入时将它们添加到模块命名空间中——即使它们没有被使用。在 Python 中,这可以通过更新 globals() 调用返回的字典来完成。

总而言之,这应该适合你:



def test():
    form_list = ['a', 'b']
    forms = {}
    for form in form_list:
        attrs = {
            'test_val': models.FloatField(),
            'test_name': models.CharField(),
            '__module__': __name__,
        }
        form_class_name = f"form_{form}" 
        form_class = type(form_class_name, (models.Model,), attrs)
        forms[form_class_name] = form_class

all_forms = test()
globals().update(all_forms)

【讨论】:

    猜你喜欢
    • 2021-09-11
    • 2022-06-17
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多