2009-08-26 2 views
43

Sono un po 'confuso sull'ereditarietà di sqlalchemy, al punto che non sono nemmeno sicuro di quale tipo di ereditarietà (tabella singola, tabella unita, calcestruzzo) dovrei usare qui. Ho una classe base con alcune informazioni condivise tra le sottoclassi e alcuni dati completamente separati. A volte, voglio i dati di tutte le classi e, a volte, solo delle sottoclassi. Ecco un esempio:Ereditarietà SQLAlchemy

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 

Come dovrei convertire questo SQLAlchemy utilizzando dichiarativa? In che modo, quindi, dovrei interrogare quali edifici sono all'interno di x>5 e y>3? O quali edifici residenziali hanno solo 1 residente?

risposta

68

Scegliere come rappresentare l'ereditarietà è principalmente un problema di progettazione del database. Per le prestazioni l'ereditarietà delle tabelle singole è solitamente la migliore. Da un buon punto di vista della progettazione del database, l'ereditarietà della tabella unita è migliore. L'ereditarietà della tabella unita consente di avere chiavi esterne per sottoclassi applicate dal database, è molto più semplice avere vincoli non nulli per i campi delle sottoclassi. L'ereditarietà delle tabelle in cemento è la peggiore di entrambi i mondi.

singola configurazione ereditarietà delle tabelle con gli sguardi dichiarativi come questo:

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) 

Per rendere più unito ereditarietà delle tabelle, è necessario aggiungere

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

alle sottoclassi.

Interrogazione è per lo più lo stesso con entrambi gli approcci:

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

per controllare quali campi vengono caricati è possibile utilizzare il metodo query.with_polymorphic().

La cosa più importante da considerare sull'utilizzo dell'ereditarietà per il datamapping è se si abbia effettivamente bisogno dell'ereditarietà o che si possa fare con l'aggregazione. L'ereditarietà sarà un problema se dovrai mai cambiare il tipo di un edificio, oppure i tuoi edifici possono avere sia aspetti commerciali che residenziali. In questi casi è meglio avere gli aspetti commerciali e residenziali come oggetti correlati.

+2

Wow, questa è un'ottima risposta. Grazie! Quindi ho confrontato le prestazioni tra le opzioni di tabella singola e unita, e ho scoperto che la seconda query [filter (Residential.num_residents == n) .count()] esegue ~ 2x più velocemente nello scenario a tabella singola (come previsto). Tuttavia, per qualche motivo la prima query su Building [filter ((Building.x> x) & (Building.y> y)). Count()] è circa il 10% più lento con la tabella singola, sebbene in realtà carichi tutti gli elementi è abbastanza comparabile (.all()). – Noah

+0

Per un problema più specifico relativo all'ereditarietà della tabella unita, consultare http: // stackoverflow.it/questions/8389606/how-can-a-sqlalchemy-class-inherit-correct-nonostante-having-a-tricky-foreignkey-r –

+1

L'unica cosa che manca è un esempio che usa l'ereditarietà del tavolo concreto - quello che sto cercando aiuto con, naturalmente :-) Dato che questa è una vecchia domanda, forse è stata aggiunta l'ereditarietà della tabella concreta dopo che questa domanda ha avuto risposta. – ThatAintWorking

10

Ants La soluzione di Aasma è molto più elegante, ma se si mantengono intenzionalmente le definizioni di Classe separate dalle definizioni di tabella, è necessario associare le classi alle tabelle con la funzione Mapper. Dopo aver definito le vostre classi, è necessario definire le tabelle:

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

Quindi è possibile mappare le tabelle alle classi:

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

quindi interagire con le classi nello stesso modo Formiche Aasma descritto .