Is it possible to merge a _BoundDeclarativeMeta instance with another?
For example:
from sqlalchemy.ext.declarative import declarative_base
Base1 = declarative_base()
Base2 = declarative_base()
# Something like this?
CombinedBase = Base1.merge(Base2)
In reality I would be subclassing orm models from each of them before merging. The problem is that I have an independent package that needs certain tables to work. Yet it is going to be used by another independent package.
A base class stores a catlog of classes and mapped tables in the Declarative system. This is called as the declarative base class. There will be usually just one instance of this base in a commonly imported module. The declarative_base() function is used to create base class. This function is defined in sqlalchemy.
function sqlalchemy.ext.declarative. has_inherited_table(cls) Given a class, return True if any of the classes it inherits from has a mapped table, otherwise return False. This is used in declarative mixins to build attributes that behave differently for the base class vs. a subclass in an inheritance hierarchy.
DeclarativeMeta is a class within the sqlalchemy. ext. declarative module of the SQLAlchemy project. declarative_base is another callable from the sqlalchemy.
For my use case this worked:
from sqlalchemy import MetaData
combined_meta_data = MetaData()
for declarative_base in [Base1, Base2]:
for (table_name, table) in declarative_base.metadata.tables.items():
combined_meta_data._add_table(table_name, table.schema, table)
Or, even better, automatically merge all declarative bases:
import gc
from sqlalchemy import MetaData
combined_meta_data = MetaData()
for declarative_base in ([obj for obj in gc.get_objects() if isinstance(obj, DeclarativeMeta)]):
for (table_name, table) in declarative_base.metadata.tables.items():
combined_meta_data._add_table(table_name, table.schema, table)
That meta data can then be used for example to create a diff to the current database:
from sqlalchemy import create_engine
from alembic.migration import MigrationContext
from alembic.autogenerate import compare_metadata
import pprint
engine = create_engine(...)
migration_context = MigrationContext.configure(engine.connect())
diff = compare_metadata(migration_context, combined_meta_data)
pprint.pprint(diff)
sqlalchemy.Table.to_metadata
appears to be a non-internal API that can copy a Table from one metadata object to another.
from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base
def merge_metadata(*original_metadata) -> MetaData:
merged = MetaData()
for original_metadatum in original_metadata:
for table in original_metadatum.tables.values():
table.to_metadata(merged)
return merged
Base1 = declarative_base()
Base2 = declarative_base()
print(merge_metadata(Base1.metadata, Base2.metadata).tables)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With