SQLAlchemy中的动态Python类定义

时间:2013-08-20 21:25:42

标签: python class sqlalchemy factory

我正在使用声明基础使用SQLAlchemy创建后端应用程序。 ORM需要大约15个表,每个表映射到SQLAlchemy中的类对象。因为这些类对象都是相同定义的,所以我认为工厂模式可以更简洁地生成类。但是,不仅必须定义这些类,还必须将它们分配给唯一的变量名,以便可以通过项目导入和使用它们。

(对不起,如果这个问题有点长,我更新了它,因为我更好地理解了这个问题。)

因为我们有很多列(~1000),所以我们在外部文本文件中定义它们的名称和类型以保持可读性。做完这种方式宣布我们的模型是这样的:

class Foo1(Base):
    __tablename___ = 'foo1'

class Foo2(Base):
    __tablename___ = 'foo2'

... etc

然后我可以通过循环外部文本文件的内容并在每个类定义上使用setattr()来添加列。

这没关系,但感觉太重复,因为我们有大约15个表。因此,我开始编写一个可以动态定义类的工厂函数。

def orm_factory(class_name):
    class NewClass(Base):
        __tablename__ = class_name.lower()
    NewClass.__name__ = class_name.upper()
    return NewClass

我再次可以遍历列并使用setattr()。当我把它放在一起时它看起来像这样:

for class_name in class_name_list:
    ORMClass = orm_factory(class_name)
    header_keyword_list = get_header_keyword_list(class_name)
    define_columns(ORMClass, header_keyword_list)

get_header_keyword_list获取列信息,define_columns执行setattr()分配。当我使用它并运行Base.metadata.create_all()时,生成的SQL模式就好了。

但是,当我尝试将这些类定义导入另一个模型时,我得到如下错误:

SAWarning: The classname 'NewClass' is already in the registry of this declarative base, mapped to <class 'ql_database_interface.IR_FLT_0'>

这一点,我现在意识到基于我昨天所学到的东西是完全有道理的:Python class variable name vs __name__

您可以在工厂函数中使用type作为类生成器来解决此问题(如下面的两个答案所示)。但是,这并没有解决能够导入类的问题,因为在工厂函数中动态构造类时,该函数的输出所分配的变量是静态的。即使它是动态的,例如字典键,它也必须位于模块名称空间中才能从另一个模块导入。有关详细信息,请参阅我的回答。

2 个答案:

答案 0 :(得分:1)

这听起来像一个粗略的想法。但是解决它很有趣,所以这就是你如何使它发挥作用。

据我了解,您的问题是您想要将动态创建的类添加到模块中。我使用模块和 init .py文件创建了一个hack。

dynamicModule / __ INIT __ PY:

import dynamic

class_names = ["One", "Two", "Three"]

for new_name in class_names:
     dynamic.__dict__['Class%s' % new_name] = type("Class%s" % (new_name), (object,), {'attribute_one': 'blah'})

dynamicModule / dynamic.py:

"""Empty file"""

test.py:

import dynamicModule
from dynamicModule import dynamic
from dynamicModule.dynamic import ClassOne

dynamic.ClassOne
"""This all seems evil but it works for me on python 2.6.5"""

__ INIT __ PY:

"""Empty file"""

答案 1 :(得分:1)

[注意,这是原始海报]

因此在经过一些思考和与人交谈之后,我决定以这种方式动态创建并将变量分配给全局名称空间中的类对象的能力,这不是Python支持的(并且可能有充分的理由) 。即使我认为我的用例不是太疯狂(抽出相同构造的类的预定义列表),它只是不受支持。

有很多问题指向在这种情况下使用字典,例如:https://stackoverflow.com/a/10963883/1216837。我想到了类似的东西,但问题是我需要模块名称空间中的那些类,所以我可以将它们导入到其他模块中。但是,添加globals() globals()['MyClass'] = class_dict['MyClass']之类似globals()似乎已经很好了,而且我的印象就是那些不喜欢使用Class1 = class_factory('class1') Class2 = class_factory('class2') ... 这样的人。

有一些黑客,例如patjenk所建议的黑客,但在某一点上,混淆和复杂性超出了静态声明每个类对象的清晰度的好处。因此,虽然它似乎重复,但我只是要写出所有的类定义。真的,这最终会非常简洁/可维护:

{{1}}