11

我有两张桌子,tablet并且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

我可以将记录添加到tabletand correspondent,并且执行 egTablet.query.first().correspondents只是返回一个空列表,正如您所期望的那样。如果我使用现有的平板电脑和通讯员 ID 手动将一行插入到我的tablet_correspondent表中,则会再次填充列表,正如您所期望的那样。

但是,如果我尝试做

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

我得到:

KeyError: 'tablet_correspondents'

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

4

2 回答 2

14

您的代码的问题在于.__init__方法。如果你debug-watch/print()对参数,你会注意到参数tablet实际上是一个实例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 创造新价值的方式。来自文档Creation of New Values

当关联代理拦截列表append()事件(或 set add()、 dictionary__setitem__()或标量赋值事件)时,它会使用其构造函数实例化“中间”对象的新实例,并将给定值作为单个参数传递。

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

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

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))
于 2012-06-20T09:12:49.343 回答
2

就像 van 说的,问题出在Association Object__init__的方法上。

事实上,如果 Tablet 或 Correspondent 类不定义__init__方法或不传递任何参数,则解决方案不起作用(预期没有参数)。

我找到了一个替代解决方案。很容易检测必须代理哪个类,因此可以将其分配给正确的字段(并且仍在添加更多关联):

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
于 2013-10-09T21:08:11.923 回答