Search code examples
pythonsqlalchemy

Python SQLAlchemy mapping column names to attributes and vice versa


I have a problem where I need to access a MS SQL DB, so naturally it's naming convention is different: TableName(Id, ColumnName1, ColumnName2, ABBREVIATED,...)

The way I got the model constructed in Python:

class TableName(Base):
    __tablename__ = 'TableName'
    
    id = Column('Id', BigInteger, primary_key=True, autoincrement=True)
    order_id = Column('OrderId', BigInteger, ForeignKey('Orders.Id'), nullable=False)
    column_name1 = Column('ColumnName1', String(50), nullable=True)
    column_name2 = Column('ColumnName2', String(50), nullable=True)
    abbreviated= Column('ABBREVIATED', String(50), nullable=False)
    ...

Then, I have a Repository:

class TableNameRepository:
    def __init__(self, connection_string: str):
        self.engine = create_engine(connection_string)
        self.Session = sessionmaker(bind=self.engine)

    def get_entry(self, order_id: int, column_name1: str) -> Optional[TableName]:
        with self.Session() as session:
            return session.query(TableName).filter_by(
                order_id=order_id, 
                column_name1=column_name1
            ).first() 

This works, however, now to my actual problem:

I have a template text file with placeholders using the SQL column names, meaning there are Id, ColumnName1, ColumnName2, ABBREVIATED strings and I need to replace them with values from the model.

Here is how I tried to approach this:

retrieved_table_entity = self.repository.get_entry(order_id, something)
attr_to_column = {
  column.key: column.name
  for column in retrieved_table_entity.__table__.columns
}

The problem is that doesnt work: It still creates keys that look like: {'Id': 'Id', 'OrderId': 'OrderId', 'ColumnName1': 'ColumnName1', ...}

And so, the next step which is:

            for attr_name, column_name in attr_to_column.items():
                value = getattr(retrieved_table_entity, attr_name, None)
                logging.debug(f"Accessing {attr_name}: {value}")
                reg_dict[column_name] = '' if value is None else str(value)

Doesnt work because the keys are not the python keys, in debug mode testing, if I manually do like: getattr(retrieved_table_entity, 'id'), it returns the value.

To summarize: I need a way to map, so that I have key/value dictionary like:

{
Id: 1
OrderID: 123
ColumnName1: 'SomeValue'
}

And then I could simply to soemthing like self.template.format(**keys_to_values_dict)

I have tried consulting with LLMs, and they keep circling around not suggesting a way to do this, I dont want to manually map the model again in this service function.

I also reviewed the Mapping API and cant find something relevant: https://docs.sqlalchemy.org/en/14/orm/mapping_api.html

For reference, my current dependencies:

sqlalchemy==2.0.36
pymssql==2.2.11 

Solution

  • Untested but something like this:

    from sqlalchemy import inspect
    old_to_new = {c.name: k for k, c in inspect(TableName).columns.items()}
    

    https://docs.sqlalchemy.org/en/20/orm/mapping_api.html#sqlalchemy.orm.Mapper.columns

    https://docs.sqlalchemy.org/en/20/orm/mapping_styles.html#inspection-of-mapper-objects