15 Stimmen

KeyError beim Hinzufügen von Objekten zum SQLAlchemy-Assoziationsobjekt

Ich habe zwei Tabellen, tablet y correspondent :

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

Ich kann Datensätze hinzufügen zu tablet y correspondent und tun z.B. Tablet.query.first().correspondents gibt einfach eine leere Liste zurück, wie zu erwarten war. Wenn ich manuell eine Zeile in meine tablet_correspondent Tabelle mit den vorhandenen Tabletten- und Korrespondenz-IDs wird die Liste erwartungsgemäß aufgefüllt.

Wenn ich jedoch versuche, die

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

Ich verstehe:

KeyError: 'tablet_correspondents'

Ich bin mir ziemlich sicher, dass ich hier etwas ziemlich Elementares übersehe.

15voto

van Punkte 66788

En Problem mit Ihrem Code ist in der .__init__ Methode. Wenn Sie debug-watch/print() der Parameter, werden Sie feststellen, dass der Parameter tablet ist eigentlich eine Instanz von Correspondent :

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

Der Grund dafür ist die Art und Weise, wie SA neue Werte schafft. Aus der Dokumentation Schaffung neuer Werte :

Wenn eine Liste append() Ereignis (oder setzen add() , Wörterbuch __setitem__() , Skalarzuweisungsereignis) vom Assoziations-Proxy abgefangen wird, instanziiert er eine neue Instanz des "Zwischen"-Objekts mit Hilfe seines Konstruktor eine neue Instanz des "Zwischen"-Objekts, wobei der angegebene Wert als einziges Argument übergeben wird.

In Ihrem Fall, wenn Sie anrufen tab.correspondents.append(cor) , le Tablet_Correspondent.__init__ wird mit einem einzigen Argument aufgerufen cor .

Lösung? Wenn Sie nur Folgendes hinzufügen möchten Correspondents zum Tablet dann ändern Sie einfach die Parameter in der __init__ . Entfernen Sie den zweiten Parameter sogar ganz.
Wenn Sie jedoch auch die cor.tablets.append(tab) verwenden, dann müssen Sie explizit die creator Argument an die association_proxy wie in der oben verlinkten Dokumentation erläutert:

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

2voto

dev_hero Punkte 164

Wie Van schon sagte, das Problem liegt in der __init__ Methode der Assoziationsobjekt .

Wenn die Klassen Tablet oder Correspondent keine __init__ Methode oder geben Sie keinen Parameter an, funktioniert die Lösung nicht (kein Argument erwartet).

Ich habe eine alternative Lösung gefunden. Es ist einfach zu erkennen, welche Klasse proxiert werden muss, so dass sie dem richtigen Feld zugewiesen werden kann (und immer noch daran arbeiten, weitere Assoziationen hinzuzufügen):

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X