2012-06-18 9 views
6

Ho due tabelle, tablet e correspondent:KeyError quando si aggiungono oggetti per oggetto associazione SQLAlchemy

class Correspondent(db.Model, GlyphMixin): 
    # PK column and tablename etc. come from the mixin 
    name = db.Column(db.String(100), nullable=False, unique=True) 
    # association proxy 
    tablets = association_proxy('correspondent_tablets', 'tablet') 

def __init__(self, name, tablets=None): 
    self.name = name 
    if tablets: 
     self.tablets = tablets 


class Tablet(db.Model, GlyphMixin): 
    # PK column and tablename etc. come from the mixin 
    area = db.Column(db.String(100), nullable=False, unique=True) 
    # association proxy 
    correspondents = association_proxy('tablet_correspondents', 'correspondent') 

def __init__(self, area, correspondents=None): 
    self.area = area 
    if correspondents: 
     self.correspondents = correspondents 


class Tablet_Correspondent(db.Model): 

    __tablename__ = "tablet_correspondent" 
    tablet_id = db.Column("tablet_id", 
     db.Integer(), db.ForeignKey("tablet.id"), primary_key=True) 
    correspondent_id = db.Column("correspondent_id", 
     db.Integer(), db.ForeignKey("correspondent.id"), primary_key=True) 
    # relations 
    tablet = db.relationship(
     "Tablet", 
     backref="tablet_correspondents", 
     cascade="all, delete-orphan", 
     single_parent=True) 
    correspondent = db.relationship(
     "Correspondent", 
     backref="correspondent_tablets", 
     cascade="all, delete-orphan", 
     single_parent=True) 

    def __init__(self, tablet=None, correspondent=None): 
     self.tablet = tablet 
     self.correspondent = correspondent 

posso aggiungere record a tablet e correspondent, e facendo per esempio Tablet.query.first().correspondents restituisce semplicemente una lista vuota, come ci si aspetterebbe. Se inserisco manualmente una riga nella mia tabella tablet_correspondent utilizzando il tablet e gli ID corrispondenti esistenti, l'elenco viene popolato, sempre come previsto.

Tuttavia, se provo a fare

cor = Correspondent.query.first() 
tab = Tablet.query.first() 
tab.correspondents.append(cor) 

ottengo:

KeyError: 'tablet_correspondents' 

Sono abbastanza sicuro che sto lasciando fuori qualcosa di abbastanza elementare qui.

+0

Credo che la vostra '__tablename__ = "tablet_correspondent"' è sbagliato.Non dovrebbe essere 'tablet_correspondents' (con un' s' alla fine)? –

risposta

10

Il problema con il codice è nel metodo .__init__. Se siete a debug-watch/print() i parametri, si noterà che il parametro tablet è in realtà un esempio di Correspondent:

class Tablet_Correspondent(db.Model): 
    def __init__(self, tablet=None, correspondent=None): 
     print "in __init__: ", tablet, correspondent 
     self.tablet = tablet 
     self.correspondent = correspondent 

La ragione di questo è il modo in SA crea nuovi valori. Dalla documentazione Creation of New Values:

Quando un elenco append() evento (o impostare add(), Dizionario setitem(), o evento assegnazione scalare) è stato intercettato dal proxy associazione, un'istanza di un nuova istanza dell'oggetto "intermedio" usando il suo costruttore , passando come argomento singolo il valore dato.

Nel tuo caso quando si chiama tab.correspondents.append(cor), il Tablet_Correspondent.__init__ si chiama con il singolo argomento cor.

Soluzione? Se si aggiunge solo Correspondents allo Tablet, è sufficiente passare i parametri in __init__. In effetti, rimuovi completamente il secondo parametro.
Se, invece, vi sarà anche utilizzato cor.tablets.append(tab), quindi è necessario usare esplicitamente l'argomento creator al association_proxy come spiegato nella documentazione legata alla sopra:

class Tablet(db.Model, GlyphMixin): 
    # ... 
    correspondents = association_proxy('tablet_correspondents', 'correspondent', creator=lambda cor: Tablet_Correspondent(correspondent=cor)) 

class Correspondent(db.Model, GlyphMixin): 
    # ... 
    tablets = association_proxy('correspondent_tablets', 'tablet', creator=lambda tab: Tablet_Correspondent(tablet=tab)) 
+0

Fantastico. Grazie mille. – urschrei

1

come Van detto, il soggiorno problema nel Metodo __init__ di Association Object.

Infatti, se le classi Tablet o Corrispondenti non definiscono un metodo __init__ o non passano alcun parametro, la soluzione non funziona (nessun argomento previsto).

Ho trovato una soluzione alternativa. E 'facile individuare quale classe deve essere proxy, in modo che possa essere assegnato al campo di destra (e ancora lavorare su l'aggiunta di più associazioni):

class Tablet_Correspondent(db.Model): 

    # ... 

    def __init__(self, proxied=None): 
     if type(proxied) is Tablet: 
      self.tablet = proxied 
     elif type(proxied) is Correspondent: 
      self.correspondent = proxied 
+0

Bel modo di evitare l'uso di lambda. Grazie! –

Problemi correlati