如何获取声明Table实例的模块的名称?

时间:2018-11-14 14:48:37

标签: python sqlalchemy

我有一个这样定义的sqlalchemy表

from sqlalchemy.sql.schema import Table

my_table = Table(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True),
    ...
)

我正在尝试检查该实例,以获取在其创建位置的模块。我尝试使用sqlalchemy.inspect(my_table).__module__my_table.__module__inspect.getmodule(my_table),但是,这三个函数都返回sqlalchemy.sql.schema,这是定义Table而不是{{1 }}已定义。

如何获取实例化my_table的模块的名称?

2 个答案:

答案 0 :(得分:1)

您可以通过子类Table来添加功能。在SQLAlchemy中,Table专门覆盖Table.__init__()使其成为无操作:

def __init__(self, *args, **kw):
    """Constructor for :class:`~.schema.Table`.
    This method is a no-op.   See the top-level
    documentation for :class:`~.schema.Table`
    for constructor arguments.
    """
    # __init__ is overridden to prevent __new__ from
    # calling the superclass constructor.

关键在于它不会调用super().__init__(),以便sqlalchemy可以接受实例化命令,而无论您执行什么操作,都需要维护。

from sqlalchemy.sql.schema import Table

class MyTable(Table):

    def __init__(self, *args, **kwargs):
        self._where_am_i = __file__

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True)
)

在这种情况下,MyTable.__init__()仍在阻止超类构造函数,但它还会向实例添加一个属性,该属性将是实例化该类的模块的名称。我专门选择了一个不太明显的属性名称(_where_am_i),该名称不太可能被sqlalchemy覆盖,并使用__file__返回模块的路径(但是您可以根据需要进行设置)。

我测试了插入并选择仍然可以使用:

import logging
from sqlalchemy.sql import select
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.basicConfig(level=logging.INFO)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
conn = engine.connect()
conn.execute(my_table.insert(), [{"my_id": i} for i in range(1, 6)])
s = select([my_table])
result = conn.execute(s)
for row in result:
    print(row)
# (1,)
# (2,)
# (3,)
# (4,)
# (5,)

和实例化位置:

print(my_table._where_am_i)  # 53302898.py (that's the name of my module).

外部模块:

# external_module.py

from sqlalchemy_app import Base
from sqlalchemy.sql.schema import Table
from sqlalchemy import Column, BigInteger

metadata = Base.metadata

class MyTable(Table):

    def __init__(self, *args, **kwargs):
        self._where_am_i = __file__

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True)
)

并且:

# 53302898.py

from external_module import my_table

if __name__ == '__main__':
    print(my_table._where_am_i)  # prints C:\Users\peter_000\OneDrive\git\test\external_module.py

请注意它如何在第一个测试中返回相对文件路径,并在外部模块测试中返回绝对文件路径。您可以在这里阅读有关内容:Python __file__ attribute absolute or relative?,但可以使_where_am_i属性返回适合您的应用程序的所有内容。

编辑 上述解决方案要求将Table内部子类化在组成实例的模块中,否则它将钉住实例化该类的模块,而不是实例。如果只想在项目中对Table进行子类化,则需要将该位置传递给构造函数。

这有效:

class MyTable(Table):

    def __init__(self, *args, _where_am_i=None, **kwargs):
        self._where_am_i = _where_am_i

...但是在实例化时会收到警告:

SAWarning: Can't validate argument '_where_am_i'; can't locate any SQLAlchemy dialect named '_where'

为避免这种情况,您必须重写sqlalchemy的备用构造函数Table._init(),删除位置参数,然后委托备份链:

class MyTable(Table):

    def _init(self, *args, _where_am_i=None, **kwargs):
        self._where_am_i = _where_am_i
        super()._init(*args, **kwargs)

从外部模块导入:

# 53302898.py
from external_module import MyTable

my_table = MyTable(
    "my_table",
    metadata,
    Column("my_id", BigInteger(), primary_key=True),
    _where_am_i = __file__
)

if __name__ == '__main__':
    print(my_table._where_am_i)  # 53302898.py

以上所有测试仍然通过。

答案 1 :(得分:0)

不能。如果您参考the Python documentation index,则会看到__module__的三个条目:一个代表a class attribute,一个代表a function attribute,另一个代表a method attribute。只有那些类型的对象才具有声明其记录的模块。 my_table都不是;它只是Table类的一个实例,因此可以在其上找到的唯一__module__Table.__module__