本文介绍了将对象添加到 SQLAlchemy 关联对象时出现 KeyError的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个表,tabletcorrespondent:

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

我可以将记录添加到 tabletcorrespondent,并执行例如Tablet.query.first().correspondents 如您所料,只是返回一个空列表.如果我使用现有的平板电脑和通讯员 ID 手动将一行插入到我的 tablet_correspondent 表中,该列表将再次按照您的预期填充.

I can add records to tablet and correspondent, and doing e.g. Tablet.query.first().correspondents simply returns an empty list, as you would expect. If I manually insert a row into my tablet_correspondent table using existing tablet and correspondent IDs, the list is populated, again as you would expect.

但是,如果我尝试这样做

However, if I try to do

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

我明白了:

KeyError: 'tablet_correspondents'

我很确定我在这里遗漏了一些相当基本的东西.

I'm pretty sure I'm leaving out something fairly elementary here.

推荐答案

您的代码的问题.__init__ 方法中.如果你要debug-watch/print()参数,你会注意到参数tablet实际上是Correspondent的一个实例:

The problem with your code is in the .__init__ method. If you are to debug-watch/print() the parameters, you will notice that the parameter tablet is actually an instance of Correspondent:

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

这样做的原因是 SA 创造新价值的方式.来自文档创建新值:

The reason for this is the way SA creates new values. From documentation Creation of New Values:

当一个列表append()事件(或设置add(),字典__setitem__(),或标量赋值事件)被关联代理拦截,它使用其实例化中间"对象的新实例构造函数,将给定值作为单个参数传递.

在您调用 tab.correspondents.append(cor) 的情况下,Tablet_Correspondent.__init__ 使用单个参数 cor 调用.

In your case when you call tab.correspondents.append(cor), the Tablet_Correspondent.__init__ is called with single argument cor.

解决方案?如果你只是将Correspondents添加到Tablet,那么只需在__init__中切换参数代码>.实际上,完全删除第二个参数.
但是,如果您还将使用 cor.tablets.append(tab),那么您需要明确地将 creator 参数用于 association_proxy> 如上面链接的文档中所述:

Solution? If you will only be adding Correspondents to the Tablet, then just switch the parameters in the __init__. In fact, remove the second parameter completely.
If, however, you will also be using cor.tablets.append(tab), then you need to explicitely use the creator argument to the association_proxy as explained in the documentation linked to above:

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

这篇关于将对象添加到 SQLAlchemy 关联对象时出现 KeyError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!