【问题标题】:SQLAlchemy 'Connection' object has no attribute '_Inspector__engine'SQLAlchemy 'Connection' 对象没有属性 '_Inspector__engine'
【发布时间】:2021-09-25 06:18:46
【问题描述】:

我想使用 SQLAlchemy 读取未映射到对象的数据库(需要访问开发时未知的数据库)。其中一项功能是读取不同表的列名。因此我写了这个连接器:

MyConnector.py

import sqlalchemy as db

class MyConnector:
    __engine = None

    def __init__(self, db_connection):
        self.__engine = db.create_engine(db_connection)

    def __get_table(self, tbl_name):
        metadata = db.MetaData()
        table = db.Table(tbl_name, metadata)
        inspector = db.inspect(self.__engine)
        inspector.reflect_table(table, None)
        return table

    def get_table_columns(self, tbl_name):
        table = self.__get_table(tbl_name)
        return table.columns.keys()

通过以下UnitTest进行测试:

ConnectorTest.py

import unittest
from MyConnector import MyConnector

class MyTestCase(unittest.TestCase):
    db_string = "sqlite:///myTest.db"
    expected_columns_user = ["id", "f_name", "l_name", "note"]
    expected_columns_address = ["id", "street", "number", "postal_code", "additional_information", "fk_user"]

    def test_get_table_columns_user(self):
        connector = MyConnector(self.db_string)
        answer = connector.get_table_columns("user")
        self.assertListEqual(self.expected_columns_user, answer)

    def test_get_table_columns_address(self):
        connector = MyConnector(self.db_string)
        answer = connector.get_table_columns("address")
        self.assertListEqual(self.expected_columns_address, answer)


if __name__ == '__main__':
    unittest.main()

SQLite DB 只包含这两个空表:

CREATE TABLE user (
    id INTEGER NOT NULL DEFAULT AUTO_INCREMENT,
    f_name VARCHAR,
    l_name VARCHAR,
    note VARCHAR,
    PRIMARY KEY (
        id
    )
);

CREATE TABLE address (
    id INTEGER NOT NULL DEFAULT AUTO_INCREMENT,
    street VARCHAR NOT NULL,
    number INTEGER,
    postal_code INTEGER NOT NULL,
    additional_information VARCHAR,
    fk_user INTEGER NOT NULL,
    PRIMARY KEY (
        id
    ),
    FOREIGN KEY (
        fk_user
    )
    REFERENCES user(id) ON DELETE CASCADE
);

测试test_get_table_columns_user 按预期工作。 测试test_get_table_columns_address 引发错误:

Error
Traceback (most recent call last):
  File "ConnectorTest.py", line 17, in test_get_table_columns_address
    answer = connector.get_table_columns("address")
  File "MyConnector.py", line 17, in get_table_columns
    table = self.__get_table(tbl_name)
  File "MyConnector.py", line 13, in __get_table
    inspector.reflect_table(table, None)
  File "venv\lib\site-packages\sqlalchemy\engine\reflection.py", line 802, in reflect_table
    reflection_options,
  File "venv\lib\site-packages\sqlalchemy\engine\reflection.py", line 988, in _reflect_fk
    **reflection_options
  File "<string>", line 2, in __new__
  File "venv\lib\site-packages\sqlalchemy\util\deprecations.py", line 298, in warned
    return fn(*args, **kwargs)
  File "venv\lib\site-packages\sqlalchemy\sql\schema.py", line 601, in __new__
    metadata._remove_table(name, schema)
  File "venv\lib\site-packages\sqlalchemy\util\langhelpers.py", line 72, in __exit__
    with_traceback=exc_tb,
  File "venv\lib\site-packages\sqlalchemy\util\compat.py", line 207, in raise_
    raise exception
  File "venv\lib\site-packages\sqlalchemy\sql\schema.py", line 596, in __new__
    table._init(name, metadata, *args, **kw)
  File "venv\lib\site-packages\sqlalchemy\sql\schema.py", line 676, in _init
    resolve_fks=resolve_fks,
  File "venv\lib\site-packages\sqlalchemy\sql\schema.py", line 705, in _autoload
    with insp._inspection_context() as conn_insp:
  File "AppData\Local\Programs\Python\Python37\lib\contextlib.py", line 112, in __enter__
    return next(self.gen)
  File "venv\lib\site-packages\sqlalchemy\engine\reflection.py", line 217, in _inspection_context
    sub_insp = self._construct(self.__class__._init_connection, conn)
  File "venv\lib\site-packages\sqlalchemy\engine\reflection.py", line 117, in _construct
    init(self, bind)
  File "venv\lib\site-packages\sqlalchemy\engine\reflection.py", line 135, in _init_connection
    self.engine = connection.__engine
AttributeError: 'Connection' object has no attribute '_Inspector__engine'

由于两个测试运行完全相同的代码,除了表名的参数,我对结果感到很困惑。

使用谷歌我找到了这个 PR:https://github.com/laughingman7743/PyAthena/pull/65/files 似乎与此问题无关(引擎,而不是连接)。

许多关于 Sessions 的帖子(例如 Python SQLAlchemy Query: AttributeError: 'Connection' object has no attribute 'contextual_connect'https://github.com/sqlalchemy/sqlalchemy/issues/4046)。这无关紧要,因为在这种情况下使用 Inspector 似乎甚至不需要调用 engine.connect()

为了排除挥之不去的锁或类似问题,我也将test_get_table_columns_address的参数更改为"user"。这适用于断言,显然不匹配。所以在我看来,表(-name?)是个问题——这很奇怪。

对 SQLAlchemy 有更多经验和洞察力的人能否指出,我的代码的问题出在哪里/是什么?提前致谢!

Python 3.7.4

SQLAlchemy 1.4.20

【问题讨论】:

  • 我无法重现该错误,但我强烈怀疑它与使用双下划线作为“私有”属性的前缀有关。我建议不要使用它们。如果要指示属性是私有的,请使用单个前导下划线。请参阅this answer 进行一些讨论。在 engineget_table 上尝试不带任何前导双下划线的代码,看看它是否有效。
  • 谢谢@snakecharmerb!无法访问我的环境,今晚将尝试。到目前为止,我发现(感谢同事),问题似乎与外键有关:根据docs.sqlalchemy.org/en/14/core/reflection.html(第二个 sn-p)元数据应该映射引用的表。尝试使用inspector.reflect_table(table, None),表明address 映射正确,忽略错误甚至使方法成功。但是在metadata.tables 中找不到表user。仍然需要尝试不同的版本...

标签: python python-3.x sqlite sqlalchemy


【解决方案1】:

太糟糕了,我无法给任何人一个更好的答案,我怀疑某些依赖项被搞砸了……我想尝试不同版本的 SQLAlchemy 来编写具有上述行为的错误报告。因此,我通过命令更改了我的 venv 几次:

pip freeze > uninstall.txt
pip uninstall -r uninstall.txt -y
pip install -r requirements.txt

同时将 requirements.txt 更改为几个不同的 SQLAlchemy 版本。请注意,requirements.txt 始终只包含一个需求(不同版本的 SQLAlchemy)。

看到每个测试版本都按预期运行并在问题的单元测试中成功,我改回 1.4.20。现在即使使用 1.4.20 版,单元测试也成功了。

为避免可能受到其他文件的影响,该项目始终只包含两个 py 文件和 db.所以我认为我们可以排除项目本身的变化作为原因。正如@snakecharmerb 指出的那样,我还尝试了___ 的“私人”机智。测试通过双下划线和单下划线成功。所以我唯一的猜测是,首先是需求出了问题。遗憾的是,我无法返回检查第一个 uninstall.txt,因为它被覆盖了很多次。

【讨论】:

    猜你喜欢
    • 2023-01-24
    • 1970-01-01
    • 2022-11-27
    • 1970-01-01
    • 2021-03-19
    • 2019-06-25
    • 2016-01-10
    • 2019-04-27
    • 2018-12-23
    相关资源
    最近更新 更多