Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy Inheritance

I'm a bit confused about inheritance under sqlalchemy, to the point where I'm not even sure what type of inheritance (single table, joined table, concrete) I should be using here. I've got a base class with some information that's shared amongst the subclasses, and some data that are completely separate. Sometimes, I'll want data from all the classes, and sometimes only from the subclasses. Here's an example:

class Building:     def __init__(self, x, y):         self.x = x         self.y = y  class Commercial(Building):     def __init__(self, x, y, business):         Building.__init__(self, x, y)         self.business = business  class Residential(Building):     def __init__(self, x, y, numResidents):         Building.__init__(self, x, y, layer)         self.numResidents = numResidents 

How would I convert this to SQLAlchemy using declarative? How, then, would I query which buildings are within x>5 and y>3? Or which Residential buildings have only 1 resident?

like image 872
Noah Avatar asked Aug 26 '09 19:08

Noah


People also ask

What is __ Mapper_args __?

The various “polymorphic” keyword arguments are specified using __mapper_args__ . This section describes some specific details on how the Declarative system interacts with SQLAlchemy ORM inheritance configuration. See Mapping Class Inheritance Hierarchies for a general introduction to inheritance mapping.

What is concrete table inheritance?

Represents an inheritance hierarchy of classes with one table per concrete class in the hierarchy. As any object purist will tell you, relational databases don't support inherit-ance - a fact that complicates object-relational mapping.

What is a SQLAlchemy relationship?

The relationship function is a part of Relationship API of SQLAlchemy ORM package. It provides a relationship between two mapped classes. This corresponds to a parent-child or associative table relationship.

Is SQLAlchemy efficient?

SQLAlchemy is very, very fast. It's just that users tend to be unaware of just how much functionality is being delivered, and confuse an ORM result set with that of a raw database cursor. They are quite different, and SQLAlchemy offers many options for controlling the mixture of "raw" vs.


2 Answers

Choosing how to represent the inheritance is mostly a database design issue. For performance single table inheritance is usually best. From a good database design point of view, joined table inheritance is better. Joined table inheritance enables you to have foreign keys to subclasses enforced by the database, it's a lot simpler to have non-null constraints for subclass fields. Concrete table inheritance is kind of worst of both worlds.

Single table inheritance setup with declarative looks like this:

class Building(Base):     __tablename__ = 'building'     id = Column(Integer, primary_key=True)     building_type = Column(String(32), nullable=False)     x = Column(Float, nullable=False)     y = Column(Float, nullable=False)     __mapper_args__ = {'polymorphic_on': building_type}  class Commercial(Building):     __mapper_args__ = {'polymorphic_identity': 'commercial'}     business = Column(String(50))  class Residential(Building):     __mapper_args__ = {'polymorphic_identity': 'residential'}     num_residents = Column(Integer) 

To make it joined table inheritance, you'll need to add

__tablename__ = 'commercial' id = Column(None, ForeignKey('building.id'), primary_key=True) 

to the subclasses.

Querying is mostly the same with both approaches:

# buildings that are within x>5 and y>3 session.query(Building).filter((Building.x > 5) & (Building.y > 3)) # Residential buildings that have only 1 resident session.query(Residential).filter(Residential.num_residents == 1) 

To control which fields are loaded you can use the query.with_polymorphic() method.

The most important thing to think about using inheritance for the datamapping, is whether you actually need inheritance or can do with aggregation. Inheritance will be a pain if you will ever need to change the type of an building, or your buildings can have both commercial and residential aspects. In those cases it's usually better to have the commercial and residential aspects as related objects.

like image 192
Ants Aasma Avatar answered Oct 09 '22 04:10

Ants Aasma


Ants Aasma's solution is much more elegant, but if you are keeping your Class definitions separate from your table definitions intentionally, you need to map your classes to your tables with the mapper function. After you have defined your classes, you need to define your tables:

building = Table('building', metadata,     Column('id', Integer, primary_key=True),     Column('x', Integer),     Column('y', Integer), ) commercial = Table('commercial', metadata,     Column('building_id', Integer, ForeignKey('building.id'), primary_key=True),     Column('business', String(50)), ) residential = Table('residential', metadata,     Column('building_id', Integer, ForeignKey('building.id'), primary_key=True),     Column('numResidents', Integer), )

Then you can map the tables to the classes:

mapper(Building, building) mapper(Commercial, commercial, inherits=Building, polymorphic_identity='commercial') mapper(Residential, residential, inherits=Building, polymorphic_identity='residential')

Then interact with the classes the exact same way Ants Aasma described.

like image 44
adam Avatar answered Oct 09 '22 03:10

adam