基于表库动态创建sqlAlchemy Metaclass

Posted

技术标签:

【中文标题】基于表库动态创建sqlAlchemy Metaclass【英文标题】:Dynamically creating sqlAlchemy Metaclass based on table library 【发布时间】:2017-10-04 22:14:47 【问题描述】:

我们在一个 ERP 系统中有几个客户。每个客户都有自己的数据库。数据库在模式方面是相同的。

不要问我为什么,但是 ERP db 没有正式定义的 PK,所以无法反映数据库...相反,我们发现声明一个 Metaclass,带有一个表声明,详细说明 PK,和自动加载的作品。一个例子:

class Customers(Base):

    __table__ = Table('Customers', Base.metadata,
                      Column('UniqueNo', Integer, primary_key=True),
                      schema = 'databaseName.schema',
                      autoload = True

关于schema = 部分的快速说明。每个数据库的架构都是相同的,但架构的命名(以及数据库名称本身)是不同的。像这样在 Metaclass 中定义模式,使我们能够跨数据库查询,能够

创建代码结构时,最简单的元类声明方法是手动。每个数据库都有一个 .py 文件,并在每个文件中执行相同的元类声明,仅更改模式,并在类名中添加后缀以避免命名混淆。像这样:

client1.py

class Customers_1(Base):

    __table__ = Table('Customers', Base.metadata,
                      Column('UniqueNo', Integer, primary_key=True),
                      schema = 'Dbclient1.client1Schema',
                      autoload = True

client2.py

class Customers_2(Base):

    __table__ = Table('Customers', Base.metadata,
                      Column('UniqueNo', Integer, primary_key=True),
                      schema = 'Dbclient2.client2Schema',
                      autoload = True

这样做是可行的,但我们希望我们可以通过仅基于一个 ERPTables.py 文件动态创建元类来减少代码量。示例:

ERPTables.py 

class Customers(Base):

    __table__ = Table('Customers', Base.metadata,
                      Column('UniqueNo', Integer, primary_key=True),
                      autoload = True

这使我们走上了元类的道路,这是一个陌生的领域。我已经到了能够动态创建元类声明的地步。但是:注册声明是我的理解不足的地方。我已经走到这一步了:

from sqlalchemy import Table
import ERPTables
import inspect

def iterate_ERPTables_Tables():
    for table in inspect.getmembers(ERPTables):
        if isinstance(table[1], Table) :
            return table[0], table[1]


dbSchemas = '_1': 'Dbclient1.client1Schema', '_2': 'Dbclient2.client2Schema'

 tables = [iterate_TableTest_Tables()]

 for key in dbSchemas:

    for table in tables:

          cls = type(table[0] + key, (Base, ), '__tablename__': table[0], '__table__': table[1])        

          break

此代码有效!唯一的问题是 SA 元类被命名为 cls。因此break。没有它,我们试图声明几个具有相同类名的元类。

我尝试了几种方法来解决这个问题,比如尝试使用不熟悉的元类视角:

dbSchemas = '_1': 'Dbclient1.client1Schema', '_2': 'Dbclient2.client2Schema'

 tables = [iterate_TableTest_Tables()]

 for key in dbSchemas:

    for table in tables:

          type(table[0] + key, (Base, ), '__tablename__': table[0], '__table__': table[1]).__new__        

完整的破解方法:

dbSchemas = '_1': 'Dbclient1.client1Schema', '_2': 'Dbclient2.client2Schema'

 tables = [iterate_TableTest_Tables()]

 for key in dbSchemas:

    for table in tables:

          exec("%s = %s" % (table[0] + key, type(table[0] + key, (Base, ), '__tablename__': table[0], '__table__': table[1])))

但是我所有的尝试都失败了,所以,在绳索的尽头,我求助于 SO,希望有人能告诉我如何解决这个问题!

PS:如果有人想知道,到目前为止,我还没有解决如何将模式从 dbSchemas 字典注入到元类中。我希望找到一种方法,但一次一个问题!

【问题讨论】:

【参考方案1】:

如果要导出类的引用,可以添加到globals()

globals()[table[0] + key] = type(...)

【讨论】:

谢谢!这是一个有趣的答案,我正在努力将问题减少到那个空间。我写了 [另一个问题] (***.com/questions/43823552/…),被称为更普遍的问题/观察。如果您能尝试回答这个问题,那就太好了。既是为了我,也是为了其他读者。看起来对这个问题很感兴趣。再次感谢您的帮助!

以上是关于基于表库动态创建sqlAlchemy Metaclass的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy 中的动态类创建

Sqlalchemy 动态创建表和映射类

sqlalchemy根据表名动态创建model类

如何使用 SQLAlchemy Postgres ORM 的声明性基础动态创建具有列名和字典约束的表?

动态 SQLAlchemy ORM 关系生成

从 R 中的数据透视表库呈现的数据透视表中删除小计和总计