Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy re-map class

I have an issue where I need to remap a class, after the corresponding table in the database is altered (columns may be added or removed).

Minimal example:

from sqlalchemy import MetaData, Column, Integer, Table, ForeignKey
from sqlalchemy.orm import mapper
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
Base.metadata = MetaData()


class TableOne(Base):
    __tablename__ = "tableone"
    id = Column(Integer, primary_key=True)


class DynamicTable(object):
    pass


def get_dynamic_table(columns):
    return Table(
        "dynamictable",
        Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("tableone_id", ForeignKey("tableone.id")),
        *[Column(name, type) for name, type in columns],
    )


config = {}
config["dynamic_columns"] = [("foo", Integer)]
initial_table = get_dynamic_table(config["dynamic_columns"])
mapper(DynamicTable, initial_table)

# Run a lot of queries involving e.g. session.query(getattr(DynamicTable, config[dynamic_columns][0]))

# Change config

config["dynamic_columns"] = [("bar", Integer)]

# .. rebuild dynamictable in backend with new config ...

# Now, DynamicTable doesn't reflect the database table

Base.metadata.remove(initial_table)  # Removes the table from metadata, but not the mapper..
updated_table = get_dynamic_table(config["dynamic_columns"])
mapper(DynamicTable, updated_table) # Raises sqlalchemy.exc.ArgumentError

The error message in full reads

Traceback (most recent call last):
  File "test.py", line 45, in <module>
    mapper(DynamicTable, updated_table)
  File "<string>", line 2, in mapper
  File "/dist/ella-python/lib/python3.7/site-packages/sqlalchemy/orm/mapper.py", line 694, in __init__
    self._configure_class_instrumentation()
  File "/dist/ella-python/lib/python3.7/site-packages/sqlalchemy/orm/mapper.py", line 1220, in _configure_class_instrumentation
    self.class_)
sqlalchemy.exc.ArgumentError: Class '<class '__main__.DynamicTable'>' already has a primary mapper defined. Use non_primary=True to create a non primary Mapper.  clear_mappers() will remove *all* current mappers from all classes.

Is there any way to re-map this (or potentially all) tables?

like image 958
Øyvind Evju Avatar asked Jul 13 '26 02:07

Øyvind Evju


1 Answers

There is an undocumented method on the Mapper-class, dispose(). This is used in clear_mappers, which has some strict warnings about usage, but the following seems to work:

from sqlalchemy.orm.mapper import class_mapper
existing_mapper = class_mapper(DynamicTable)
existing_mapper.dispose()

Base.metadata.remove(initial_table)  # Removes the table from metadata, but not the mapper..
updated_table = get_dynamic_table(config["dynamic_columns"])
mapper(DynamicTable, updated_table) # Is now re-mapped
like image 99
Øyvind Evju Avatar answered Jul 14 '26 15:07

Øyvind Evju



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!