2009-08-26 15 views
43

Estoy un poco confundido acerca de la herencia bajo sqlalchemy, hasta el punto en que ni siquiera estoy seguro de qué tipo de herencia (una sola tabla, mesa unida, concreto) debería usar aquí. Tengo una clase base con cierta información que se comparte entre las subclases y algunos datos que están completamente separados. A veces, deseo datos de todas las clases, y algunas veces solo de las subclases. Aquí hay un ejemplo:SQLAlchemy Herencia

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 

¿Cómo puedo convertir esto a SQLAlchemy usando declarativo? ¿Cómo, entonces, preguntaría qué edificios están dentro de x>5 y y>3? ¿O qué edificios residenciales tienen solo 1 residente?

Respuesta

68

Elegir cómo representar la herencia es principalmente un problema de diseño de la base de datos. Para el rendimiento, la herencia de una sola tabla suele ser la mejor. Desde un buen punto de vista de diseño de base de datos, la herencia de tablas unidas es mejor. La herencia de tabla unida le permite tener claves foráneas a las subclases impuestas por la base de datos, es mucho más simple tener restricciones no nulas para los campos de las subclases. La herencia de mesa concreta es una de las peores de ambos mundos.

configuración herencia de tablas individual con miradas declarativas como este:

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) 

Para hacer que se unió a la herencia de tablas, tendrá que añadir

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

a las subclases.

Consulta es principalmente lo mismo con los dos enfoques:

# 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) 

para controlar qué campos se cargan puede utilizar el método query.with_polymorphic().

Lo más importante para pensar en utilizar la herencia para el mapeo de datos, es si realmente necesita herencia o puede hacerlo con la agregación. La herencia será dolorosa si alguna vez necesita cambiar el tipo de un edificio, o sus edificios pueden tener aspectos tanto comerciales como residenciales. En esos casos, generalmente es mejor tener los aspectos comerciales y residenciales como objetos relacionados.

+2

Guau, esta es una gran respuesta. ¡Gracias! Así que he comparado el rendimiento entre las opciones de tabla única y unida, y encuentro que la segunda consulta [filter (Residential.num_residents == n) .count()] se ejecuta ~ 2 veces más rápido en el escenario de tabla única (como se esperaba). Sin embargo, por alguna razón, la primera consulta contra Building [filter ((Building.x> x) & (Building.y> y)). Count()] es aproximadamente un 10% más lenta con la tabla única, aunque en realidad carga todos los elementos es bastante comparable (.all()). – Noah

+0

Para un problema más específico relacionado con la herencia de tablas unidas, vea http: // stackoverflow.com/questions/8389606/how-can-a-sqlalchemy-class-inherit-properly-despite-having-a-tricky-foreignkey-r –

+1

Lo único que falta es un ejemplo que utiliza la herencia concreta de tablas, la que estoy buscando ayuda con, naturalmente :-) Dado que esta es una pregunta antigua, tal vez se agregó la herencia de tabla concreta después de que se respondió esta pregunta. – ThatAintWorking

10

Hormigas La solución de Aasma es mucho más elegante, pero si mantiene sus definiciones de clases separadas de las definiciones de su tabla intencionalmente, necesita asignar sus clases a sus tablas con la función de correlacionador. Después de haber definido sus clases, es necesario definir las tablas:

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), 
)

A continuación, puede asignar las tablas a las clases:

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

Entonces interactúan con las clases de la misma manera exacta hormigas Aasma describió .