SQLAlchemy中的动态Python类定义 [英] Dynamic Python Class Definition in SQLAlchemy

查看:60
本文介绍了SQLAlchemy中的动态Python类定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm creating a backend application with SQLAlchemy using the declarative base. The ORM requires about 15 tables each of which maps to a class object in SQLAlchemy. Because these class objects are all defined identically I thought a factory pattern could produce the classes more concisely. However, these classes not only have to be defined, they have to be assigned to unique variable names so they can be imported and used through the project.

(抱歉,问题有点长,我对问题有所了解后对其进行了更新。)

(Sorry if this question is a bit long, I updated it as I better understood the problem.)

因为我们有很多列(〜1000),所以我们在外部定义了它们的名称和类型文本文件以使内容可读。完成声明模型的一种方法是这样的:

Because we have so many columns (~1000) we define their names and types in external text files to keep things readable. Having done that one way to go about declaring our models is like this:

class Foo1(Base):
    __tablename___ = 'foo1'

class Foo2(Base):
    __tablename___ = 'foo2'

... etc

然后我可以通过循环遍历外部文本文件的内容并使用 setattr()

and then I can add the columns by looping over the contents of the external text file and using the setattr() on each class definition.

可以,但是由于我们有大约15张桌子,所以感觉太重复了。所以我反而写了一个工厂函数来动态定义类。

This is OK but it feels too repetitive as we have about 15 tables. So instead I took a stab at writing a factory function that could define the classes dynamically.

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

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

Again I can just loop over the columns and use setattr(). When I put it together it looks like this:

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模式就可以了。

Where get_header_keyword_list gets the column information and define_columns performs the setattr() assignment. When I use this and run Base.metadata.create_all() the SQL schema get generated just fine.

但是,然后尝试将这些类定义导入另一个模型时,出现如下错误:

But, when I then try to import these class definitions into another model I get an error like this:

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

根据我昨天学到的知识,我现在意识到这是完全有道理的: Python类变量名称与__name__

This, I now realize makes total sense based on what I learned yesterday: Python class variable name vs __name__.

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

You can address this by using type as a class generator in your factory function (as two of the answers below do). However, this does not solve the issue of being able to import the class because the while the classes are dynamically constructed in the factory function the variable the output of that function is assigned to is static. Even if it were dynamic, such as a dictionary key, it has to be in the module name space in order to be imported from another module. See my answer for more details.

推荐答案

[注意,这是原始海报]

[Note, this is the original poster]

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

So after some thinking and talking to people I've decided that that ability to dynamically create and assign variables to class objects in the global name space in this way this just isn't something Python supports (and likely with good reason). Even though I think my use case isn't too crazy (pumping out predefined list of identically constructed classes) it's just not supported.

在这样的情况下,有很多问题指向使用字典,例如: https://stackoverflow.com/a/10963883/1216837 。我想到了类似的方法,但问题是我需要模块名称空间中的那些类,以便可以将它们导入其他模块。但是,使用 globals() globals()['MyClass'] = class_dict ['MyClass'] 似乎那里的天气越来越好了,我的印象是人们对使用 globals()这样不满意。

There are lots of questions that point towards using a dictionary in a case like this, such as this one: https://stackoverflow.com/a/10963883/1216837. I thought of something like that but the issue is that I need those classes in the module name space so I can import them into other modules. However, adding them with globals() like globals()['MyClass'] = class_dict['MyClass'] seems like it's getting pretty out there and my impression is people on SO frown on using globals() like this.

存在一些黑客行为,例如 patjenk 所建议的黑客行为,但在某种程度上,混淆和复杂性突出了黑客的好处。静态声明每个类对象的清晰度。因此,尽管看起来很重复,但我将写出所有的类定义。确实,这最终是非常简洁/可维护的:

There are hacks such as the one suggested by patjenk but at a certain point the obfuscation and complexity out weight the benefits of the clarity of declaring each class object statically. So while it seems repetitive I'm just going to write out all the class definitions. Really, this end up being pretty concise/maintainable:

Class1 = class_factory('class1')
Class2 = class_factory('class2')
...

这篇关于SQLAlchemy中的动态Python类定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆