Consider the following code which creates a very simple table (without using SQLAlchemy), then adds an entry to it using SQLAlchemy ORM and retrieves it:
import sqlite3
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
DB_PATH = '/tmp/tst.db'
#create a DB
sqlite_conn = sqlite3.connect(DB_PATH)
sqlite_conn.execute('''CREATE TABLE tst (
id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
c0 INTEGER,
c1 INTEGER
);''')
sqlite_conn.commit()
#intialize an SA engine/session/mapped class
engine = create_engine('sqlite:///{}'.format(DB_PATH))
Base = declarative_base()
Base.metadata.reflect(bind=engine)
Session = sessionmaker(bind=engine)
class Tst(Base):
__table_name__ = 'tst'
__table__ = Base.metadata.tables[__table_name__]
columns = list(__table__.columns)
field_names = [c.name for c in columns]
#add an entry to the table
session = Session()
inst = Tst()
session.add(inst)
session.commit()
#retrieve an entry from the table
session = Session()
inst = session.query(Tst).first()
print inst.c1
One may expect that the code above will just print 'None', as 'c1' was not assigned a value. Instead of it, I'm getting the following error message:
Traceback (most recent call last):
File "...", line 39, in <module>
print inst.c1
AttributeError: 'Tst' object has no attribute 'c1'
But if the following line will be removed/commented:
field_names = [c.name for c in columns]
the output will be as expected.
In general, it looks like the iteration over Table.columns
inside the class definition will cause the last column to be omitted from the class instances.
Following this answer, I actually changed the code to use Inspector
, and it worked fine. However, AFAIK, accessing Table.columns
is completely legitimate, so I wanted to understand whether it's buggy behavior or something wrong on my side.
P.S. tested with SQLAlchemy 1.1.9
P.P.S. the issue doesn't appear to be related to a specific DB dialect - reproduced with MySQL, sqlite.