I want to use SQLAlchemy to read databases, which are not mapped to Objects (needs to access DBs unknown at time of development). One of the functionalities is to read the column names of different tables. Therefore I wrote this Connector:
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()
Which is tested by following 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 contains only these two empty tables:
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
);
The test test_get_table_columns_user
works as expected.
The test test_get_table_columns_address
raises an error:
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'
Since both tests run exactly identical code, except the parameter for the table name, I am pretty confused by the outcome.
Using Google I found this PR: https://github.com/laughingman7743/PyAthena/pull/65/files Seems not relevant to this Problem (Engine, not Connection).
Many posts about Sessions (like Python SQLAlchemy Query: AttributeError: 'Connection' object has no attribute 'contextual_connect' or https://github.com/sqlalchemy/sqlalchemy/issues/4046). Which should not matter because using the Inspector in this context seems not even to need a call to engine.connect()
.
For the sake of ruling out lingering locks or similar problems, I also changed the parameter of test_get_table_columns_address
to "user"
. This works up to the assert, which obviously does not match. So it looks like a problem with the table (-name?) to me - which is pretty weird.
Could someone with more experience and insight to SQLAlchemy point out, where/what the Problem with my code is? Thanks in advance!
Python 3.7.4
SQLAlchemy 1.4.20