python - Herencia de SQLAlchemy
inheritance (2)
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:
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 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?
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.
La configuración de herencia de tabla única con declaración se ve así:
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 una a la herencia de tablas, deberás agregar
__tablename__ = ''commercial''
id = Column(None, ForeignKey(''building.id''), primary_key=True)
a las subclases.
Las consultas son básicamente las mismas con ambos 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 están cargados, puede usar 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.
La solución de Ants Aasma es mucho más elegante, pero si mantiene sus definiciones de Clase separadas de las definiciones de su tabla intencionalmente, necesita asignar sus clases a sus tablas con la función del asignador. Después de haber definido sus clases, debe definir sus 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),
)
Luego 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'')
Luego, interactúa con las clases de la misma manera que describió Ants Aasma.