基于 table 库动态创建 sqlAlchemy 元类

Dynamically creating sqlAlchemy Metaclass based on table library

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

不要问我为什么,但是 ERP 数据库没有正式定义的 PK,因此不可能反映数据库...相反,我们发现声明一个 Metaclass,用table 声明,详细说明 PK 和自动加载工作。一个例子:

class Customers(Base):

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

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

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

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 文件动态创建 Metaclasses 来减少代码量。示例:

ERPTables.py 

class Customers(Base):

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

这让我们走上 Metaclasses 的道路,这是一个陌生的领域。我已经到了可以动态创建 metaclass 声明的地步。但是:注册声明是我理解不足的地方。我已经走到这一步了:

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 Metaclass 被命名为 cls。因此 break。如果没有它,我们将尝试声明多个具有相同 class 名称的 Metaclasses。

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

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__        

完全成熟的 hack 解决方法:

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 词典中的模式注入 Metaclass。我希望找到一种方法,但一次一个问题!

如果要导出对 类 的引用,可以将它们添加到 globals():

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