Skip to content

EntityName

Federico Caselli edited this page Jun 6, 2020 · 4 revisions

Entity Name

"entity_name" was a feature in very early versions of SQLAlchemy, through version 0.4, which allowed a class to be fully mapped multiple times to entirely different Table objects. A string entity_name parameter would be passed to most Session methods indicating which mapping should be in use.

This approach was removed early on as it was extremely difficult to maintain and was never used. It also didn't really work, because a SQLAlchemy mapping makes modifications to the mapped class, so it's not really feasible to have many mappers against the exact same class, especially if these mappers intend to have differing behaviors or columns.

The entity_name feature was adapted from Hibernate, where in the Java world creating a subclass is a very explicit and rigid affair. It turns out that in Python, this is not at all the case. Using type we can easily make anonymous subclasses for each desired mapping. Here's first a "classical mapping" approach:

from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData(create_engine('sqlite://', echo=True))
t1 = Table('t1', metadata, Column('id', Integer, primary_key=True))
t2 = Table('t2', metadata, Column('id', Integer, primary_key=True))
metadata.create_all()

def map_class_to_some_table(cls, table, entity_name, **kw):
     newcls = type(entity_name, (cls, ), {})
     mapper(newcls, table, **kw)
     return newcls

class Foo(object):
    pass

T1Foo = map_class_to_some_table(Foo, t1, "T1Foo")
T2Foo = map_class_to_some_table(Foo, t2, "T2Foo")

sess = sessionmaker()()

sess.add_all([T1Foo(), T1Foo(), T2Foo(), T1Foo()])

print(sess.query(T1Foo).all())
print(sess.query(T2Foo).all())
    

With Declarative, we can use mixin classes:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class TBase(object):
    """Base class is a 'mixin'.

    Guidelines for declarative mixins is at:

    http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixin-classes

    """
    id = Column(Integer, primary_key=True)
    data = Column(String(50))

    def __repr__(self):
        return "%s(data=%r)" % (
            self.__class__.__name__, self.data
        )

# build classes explicitly...
class T1Foo(TBase, Base):
    __tablename__ = 't1'

class T2Foo(TBase, Base):
    __tablename__ = 't2'

    timestamp = Column(DateTime, default=func.now())


# or generate them with type()
T3Foo = type("T3Foo", (TBase, Base), {"__tablename__": "t3"})

engine = create_engine('sqlite://', echo=True)

Base.metadata.create_all(engine)

sess = sessionmaker(engine)()

sess.add_all([T1Foo(data='t1'), T3Foo(data='t2'), T2Foo(data='t3'),
             T1Foo(data='t4')])

print(sess.query(T1Foo).all())
print(sess.query(T2Foo).all())
print(sess.query(T3Foo).all())
Clone this wiki locally