2014-10-28 7 views
6

Questo è relativo a questa domanda converting to declarative method and column property, a cui non è mai stata data risposta.SQLAlchemy Come mappare una singola colonna della relazione uno a uno usando dichiarativo

Stiamo provando a impostare un progetto Flask-SQLAlchemy su uno schema esistente (uno non possiamo cambiare) e abbiamo deciso sulla sintassi dichiarativa in modo da poter organizzare le classi in più file in modo sano per la manutenzione. Questo funziona per la maggior parte delle nostre relazioni tranne per qualcosa che chiamiamo, per mancanza di un termine migliore, tabelle di attributi. Si tratta di tabelle a foglia singola su alcuni oggetti primari e in genere contengono un tipo di vocabolario controllato per l'attributo. L'obiettivo nell'ORM è di mappare tutti questi tipi (di cui ci sono molti) tipi di tabelle come se fossero proprietà della tabella primaria.

Ecco un esempio SQA con due tavoli:

class MarkerType(db.Model): 
    __tablename__="mrk_types" 
    _marker_type_key = db.Column(db.Integer,primary_key=True) 
    name = db.Column(db.String()) 

class Marker(db.Model): 
    __tablename__="mrk_marker" 
    _marker_key=db.Column(db.Integer,primary_key=True) 
    _marker_type_key=db.Column(db.Integer()) 

si vuole accedere MarkerType.name come se dicevamo Marker.markertype, o in una query come Marker.markertype == 'cosa'. L'unico modo ho potuto gestire che è con un column_property nella classe Marker, in questo modo:

markertype = db.column_property(
      db.select([MarkerType.name]). 
      where(MarkerType._marker_type_key==_marker_type_key) 
    ) 

Tuttavia, non riesco a trovare il modo di fare questo in modo dichiarativo, e forse in questo modo non esiste. C'è un modo sano per raggiungere questo obiettivo senza dovermi preoccupare delle mie importazioni o, ancora peggio, dell'ordine delle mie lezioni? Dato che abbiamo centinaia di tabelle da mappare, posso vedere che si tratta di un incubo di manutenzione se dobbiamo preoccuparci della classe e dell'ordine di importazione.

Se tutto ciò è totalmente impossibile, pio desiderio, qual è un approccio migliore per mappare queste tabelle?

+0

Hai esaminato gli esempi ['Vertical Attribute Mapping'] (http://docs.sqlalchemy.org/en/rel_0_9/orm/examples.html#module-examples.vertical)? – van

+0

No, ma non sono sicuro che la mia situazione si applichi. Non ho una tabella figlio con coppie di valori chiave. È un tavolo per bambini con solo un valore. Non sono sicuro che sia ancora considerato un tavolo verticale. Inoltre, gli esempi per l'impostazione di assoc_proxy non sembrano dichiarativi. – kevinrstone

risposta

1

Questo suona come un caso d'uso eccellente per il Association Proxy. Questo genera un campo di un modello correlato. In questo caso l'applicazione sarebbe:

from sqlalchemy.orm import relationship 
from sqlalchemy.ext.associationproxy import association_proxy 

class MarkerType(db.Model): 
    __tablename__="mrk_types" 
    _marker_type_key = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String()) 

class Marker(db.Model): 
    __tablename__="mrk_marker" 
    _marker_key=db.Column(db.Integer,primary_key=True) 
    _marker_type_key=db.Column(db.Integer, ForeignKey('mrk_types._marker_type_key') 

    mt = relationship(MarkerType, uselist=False) 
    marker_type = association_proxy('mt', 'name') 

Questo permette inviando una richiesta come session.query(Marker).filter_by(marker_type='my_marker_type')

Il campo marker_type è un proxy del campo name sull'oggetto MarkerType. Tale oggetto può essere referenziato dal campo mt (il campo della relazione)

Nota lo uselist=False. Questo indica che ogni Marker ha 1 tipo di marker. La relazione rileva automaticamente ForeignKey e la usa.

+0

Sì, sembra esattamente quello di cui avevo bisogno. Grazie. – kevinrstone

1

Quindi da quello che raccolgo, sei bloccato con due tavoli. Uno con un intero, uno con una stringa, col.

Class Marker 
    _marker_key_ primary_key 
    # _ = Integer ## This can be ignored as its unnecessary. 

L'altro ha un

Class MarkerType 
    _marker_type_key = primary_key 
    name = String 

Come ho letto, si vuole classe Marker di avere molte corde Classe MarkerType che si può facilmente manipolare o disporrà. Però, non sono sicuro che sia quello che vuoi.

Se lo è, è possibile ottenere questo presupponendo che si controlli il seeding del database. È possibile creare una bandiera all'inizio di ciascun nome che punta alla chiave primaria Markers.

Esempio: MarkerType.name = 10324_Orange

non ho familiarità nell'uso SQLAlchemy senza sessioni, e non mi sento di fare la ricerca, quindi sono solo andando scrivere la mia risposta supponendo che si sta utilizzando SQLAlchemy sessioni, in modo da poter ottenere il concetto e in grado di regolare se necessario.

### !!! ASSUME 'create_session' method exists that 
#### creates a sqlalchemy session instance 

Class Marker: 
    # ... initialize and such 
    # ... then add these helper methods 

    ## Get all properties linked to this primary table row 
    def marker_types(): 
     return db.query(MarkerType). 
      filter(MarkerType.name.like(str(self._marker_key_)+"_%")).all() 
    ## Get specific property linked to this primary table row 
    def marker_type(marker_type_name): 
     db = create_session() 
     marker_type_list = db.query(MarkerType). 
      filter(MarkerType.name.like(str(self._marker_key_)+"_%") 
      AND marker_type_name == MarkerType.name).first() 
     db.close() 
     return marker_type_list 

    def update_marker_type(old_val, new_val) 
     db = create_session() 
     updated_marker_type = marker_type(old_val) 
     updated_marker_type.name = str(self._marker_key_)+" "+new_val 
     db.close() 
     return True 

    def create_marker_type(val) 
     marker_type = MarkerType(name = str(self._marker_key_)+" "+val) 
     db = create_session() 
     db.add(marker_type) 
     db.commit() 
     db.close() 
     return marker_type._marker_type_key 

Da qui è possibile aggiungere ulteriori flag alla stringa nome. Cose come il tipo di attributo.

Marker.id = 193 

MarkerType.id = 1 
MarkerType.name = "193_color_Black" 
MarkerType.id = 2 
MarkerType.name = "193_style_Fine" 

Questo flag aggiuntivo può permettere di cercare attributi generali nomi specifici collegati al tuo riga specifica, ed è di gran lunga più utilizzabile, anche se un po 'più complicato. Dipende davvero dal tuo caso d'uso.

+0

È passato un po 'di tempo, ma non l'ho mai veramente risolto. Scusa, la tua risposta non è quella che stavo cercando. Volevo davvero un modo per definire column_property come ho mostrato, ma con la sintassi della stringa in modo da non dover importare la classe MarkerType. Puoi già farlo con le relazioni e alcune altre cose. – kevinrstone

+0

Nessun problema ... qualcuno ci ha messo una taglia, quindi ho pensato di provare qualcosa. Non era esattamente chiaro su quello che stavi cercando, ma ho pensato di dargli il via per collegare ufficiosamente i tavoli. – ExperimentsWithCode

+0

Non penso che tu abbia bisogno di importare markertype se è una risorsa di marker, e importato dove è definita quella classe. Soprattutto se si interagisce solo con i metodi incorporati nella classe del proprio marcatore. – ExperimentsWithCode

1

Utilizzare un relationship per consentire l'accesso al marker_type dal tavolo Marker e specificare un vincolo ForeignKey modo SQLAlchemy capisce la relazione tra le tabelle.

Ciò consente di accedere facilmente all'attributo MarkerType da un record Marker nonché di eseguire una query su MarkerType.name. Il seguente mostra l'inserimento di due record e il filtraggio in base all'attributo name.

>>> db.session.add(Marker(marker_type=MarkerType(name="blue"))) 
>>> db.session.add(Marker(marker_type=MarkerType(name="red"))) 
>>> db.session.commit() 


>>> markers = Marker.query.all() 
>>> print({m._marker_key: m.marker_type.name for m in markers}) 

{1: 'blue', 2: 'red'} 

>>> result = Marker.query.filter(Marker._marker_type_key==MarkerType._marker_type_key) \ 
...      .filter(MarkerType.name=='blue').all() 
>>> print({m._marker_key: m.marker_type.name for m in result}) 

{1: 'blue'} 

L'ordine di dichiarazione delle classi fa non materia e le classi hanno bisogno non essere dichiarato insieme. Tuttavia, lo schema deve essere registrato rispetto alla stessa istanza di db e quando si esegue una query sulle tabelle, è necessario importare le classi di tabelle che si riferiscono.

Dopo aver aggiunto il relationship e ForeignKey al marcatore schema dichiarativa sarebbe diventato:

class MarkerType(db.Model): 
    __tablename__="mrk_types" 
    _marker_type_key = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String()) 

class Marker(db.Model): 
    __tablename__="mrk_marker" 
    _marker_key=db.Column(db.Integer, primary_key=True) 
    _marker_type_key=db.Column(db.Integer(), db.ForeignKey('mrk_types._marker_type_key')) 
    marker_type=db.relationship('MarkerType') 
+0

Questo tuttavia non consente di interrogare direttamente il tipo di marcatore come una stringa, come indicato dall'OP –

+0

@JesseBakker effettivamente credo che lo faccia. Ho aggiunto un esempio. – user650881

Problemi correlati