2012-06-18 13 views
6

Mam dwie tabele, tablet i correspondent:KeyError podczas dodawania obiektów do sqlalchemy stowarzyszenia obiektu

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 

mogę dodać rekordy do tablet i correspondent i robi np Tablet.query.first().correspondents po prostu zwraca pustą listę, jak można się spodziewać. Jeśli ręcznie wstawiam wiersz do tabeli tablet_correspondent przy użyciu istniejących identyfikatorów tabletu i korespondenta, lista zostanie wypełniona ponownie, jak można się spodziewać.

Jednak, gdy próbuję zrobić

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

uzyskać:

KeyError: 'tablet_correspondents' 

Jestem pewien, że odchodzę coś dość elementarny tutaj.

+0

myślę, że '__tablename__ =„tablet_correspondent”' jest źle.Czy nie powinien to być "tablet_correspondents" (z 's' na końcu)? –

Odpowiedz

10

Kod z kodem jest w metodzie .__init__. Jeśli mają debug-watch/print() parametry, można zauważyć, że parametr tablet jest faktycznie instancja Correspondent:

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

Powodem tego jest droga SA tworzy nowe wartości. Z dokumentacji Creation of New Values:

Kiedy Dołącz list() zdarzenie (lub zestaw add(), słownik setitem() lub skalarne wydarzenie cesja) jest przechwytywany przez pełnomocnika stowarzyszenia, to instancję nową instancję obiektu "pośredniczącego" za pomocą konstruktora , przekazując jako pojedynczy argument podaną wartość.

W twoim przypadku gdy dzwonisz tab.correspondents.append(cor) The Tablet_Correspondent.__init__ jest wywoływana z jednym argumentem cor.

Rozwiązanie? Jeśli dodamy tylko Correspondents do Tablet, wystarczy zmienić parametry w __init__. W rzeczywistości całkowicie usuń drugi parametr.
Jeśli jednak będzie również przy użyciu cor.tablets.append(tab), to trzeba jawnie użyć creator argument do association_proxy jak wyjaśniono w dokumentacji związanej z wyżej:

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

Fantastyczne. Dzięki wielkie. – urschrei

1

jak van powiedział, pobyt problemem w __init__ Metoda z Association Object.

W rzeczywistości, jeśli klasy tabletu lub korespondenta nie definiują metody __init__ lub nie przekazują żadnych parametrów, rozwiązanie nie działa (nie oczekuje się żadnego argumentu).

Znalazłem alternatywne rozwiązanie. Jest to łatwe do wykrycia, która klasa ma być serwer proxy, dzięki czemu może być przypisany do prawego pola (i nadal pracować nad dodając więcej skojarzeń):

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

Niezły sposób na unikanie używania lambd. Dzięki! –

Powiązane problemy