2009-08-26 4 views
43

Ich bin ein wenig verwirrt über die Vererbung unter sqlalchemy, bis zu dem Punkt, wo ich nicht einmal sicher bin, welche Art der Vererbung (einzelne Tabelle, gefügten Tabelle, Beton) ich hier verwenden sollte. Ich habe eine Basisklasse mit einigen Informationen, die unter den Unterklassen geteilt werden, und einige Daten, die vollständig getrennt sind. Manchmal möchte ich Daten aus allen Klassen und manchmal nur aus den Unterklassen. Hier ist ein Beispiel:SQLAlchemy Vererbung

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 

Wie würde ich dies zu SQLAlchemy mit deklarativen konvertieren? Wie würde ich dann fragen, welche Gebäude innerhalb von x>5 und y>3 liegen? Oder welche Wohngebäude haben nur 1 Bewohner?

Antwort

68

Die Entscheidung, wie die Vererbung dargestellt wird, ist meist ein Problem mit der Datenbankgestaltung. Für die Leistung ist die Vererbung einzelner Tabellen normalerweise am besten. Aus guter Sicht ist die Vererbung von verknüpften Tabellen besser. Bei der Vererbung von verknüpften Tabellen können Fremdschlüssel für Unterklassen von der Datenbank erzwungen werden. Es ist viel einfacher, Nicht-Null-Einschränkungen für Unterklassenfelder zu haben. Die Vererbung von Betontischen ist die schlimmste von beiden Welten.

Einzel Tabelle Vererbung Setup mit deklarative sieht wie folgt aus:

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) 

Um es Tisch Erbe verbunden sind, werden Sie

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

zu den Unterklassen hinzufügen müssen.

Querying ist meist das gleiche mit beiden Ansätzen:

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

Um zu steuern, welche Felder geladen Sie die query.with_polymorphic() Methode verwenden können.

Die wichtigste Sache, über die Verwendung der Vererbung für das Datamapping nachzudenken, ist, ob Sie tatsächlich Vererbung benötigen oder mit Aggregation tun können. Die Vererbung wird ein Schmerz sein, wenn Sie jemals den Typ eines Gebäudes ändern müssen, oder Ihre Gebäude können sowohl kommerzielle als auch Wohnaspekte haben. In diesen Fällen ist es in der Regel besser, die kommerziellen und die Wohnaspekte als verwandte Objekte zu betrachten.

+2

Wow, das ist eine gute Antwort. Vielen Dank! Also habe ich die Leistung zwischen den Optionen für einzelne und verbundene Tabellen verglichen und festgestellt, dass die zweite Abfrage [filter (Residential.num_residents == n) .count()] im Szenario mit einer einzelnen Tabelle ~ 2x schneller ausgeführt wird (wie erwartet). Aus irgendeinem Grund ist die erste Abfrage gegen Building [filter ((Building.x> x) & (Building.y> y)). Count()] in der einzelnen Tabelle ungefähr 10% langsamer, lädt aber tatsächlich alle Elemente ist ziemlich vergleichbar (.all()). – Noah

+0

Für ein spezifischeres Problem in Bezug auf die Vererbung von verknüpften Tabellen siehe http: // stackoverflow.com/questions/8389606/how-can-a-sqlalchemy-Klasse-erben-richtig-trotz-haben-eine-tricky-Fremdschlüssel-r –

+1

Das einzige, was fehlt, ist ein Beispiel mit konkreten Tabelle Vererbung - die, die ich suche Hilfe bei, natürlich :-) Da dies eine alte Frage ist, wurde vielleicht eine konkrete Tabellenvererbung hinzugefügt, nachdem diese Frage beantwortet wurde. – ThatAintWorking

10

Ameisen Aasmas Lösung ist viel eleganter, aber wenn Sie Ihre Klassendefinitionen absichtlich von Ihren Tabellendefinitionen getrennt halten, müssen Sie Ihre Klassen mit der Mapper-Funktion Ihren Tabellen zuordnen. Nachdem Sie Ihre Klassen definiert haben, müssen Sie Ihre Tabellen definieren:

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

Dann können Sie die Tabellen zu den Klassen zuzuordnen:

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

Dann mit den Klassen interagieren beschrieben genau die gleiche Art und Weise Ameisen Aasma .