2012-06-18 37 views
6

Tôi có hai bảng, tabletcorrespondent:KeyError khi thêm đối tượng để SQLAlchemy đối tượng liên

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 

tôi có thể thêm hồ sơ để tabletcorrespondent, và làm ví dụ Tablet.query.first().correspondents chỉ cần trả về một danh sách trống, như bạn mong đợi. Nếu tôi chèn một hàng vào bảng tablet_correspondent của tôi bằng cách sử dụng bảng hiện tại và ID người tương ứng, danh sách sẽ được điền, một lần nữa như bạn mong đợi.

Tuy nhiên, nếu tôi cố gắng làm

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

tôi nhận được:

KeyError: 'tablet_correspondents' 

Tôi chắc chắn rằng tôi sẽ đi ra một cái gì đó khá tiểu học ở đây.

+0

Tôi nghĩ '__tablename__ =" tablet_correspondent "của bạn sai.Không phải nó là 'tablet_correspondents' (với' s' ở cuối)? –

Trả lời

10

Vấn đề với mã của bạn ở phương thức .__init__. Nếu bạn là để debug-watch/print() các thông số, bạn sẽ nhận thấy rằng các tham số tablet thực sự là một thể hiện của Correspondent:

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

Lý do cho điều này là cách SA tạo ra giá trị mới. Từ tài liệu Creation of New Values:

Khi một append list() sự kiện (hoặc thiết lập add(), từ điển SetItem(), hoặc kiện phân vô hướng) bị chặn bởi proxy hiệp hội, nó instantiates một trường hợp mới đối tượng “trung gian” sử dụng phương thức khởi tạo của nó, chuyển như một đối số duy nhất với giá trị đã cho.

Trong trường hợp của bạn khi bạn gọi tab.correspondents.append(cor), số Tablet_Correspondent.__init__ được gọi với một đối số cor.

Giải pháp? Nếu bạn chỉ thêm Correspondents vào Tablet, thì chỉ cần chuyển các tham số trong __init__. Trong thực tế, loại bỏ các tham số thứ hai hoàn toàn.
Tuy nhiên, nếu bạn cũng sẽ được sử dụng cor.tablets.append(tab), thì bạn cần phải explicitely sử dụng creator luận cho association_proxy như được giải thích trong tài liệu liên quan đến ở trên:

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

Tuyệt vời. Cám ơn rất nhiều. – urschrei

1

Giống như van cho biết, vấn đề sẽ nằm trong vùng __init__ phương thức của Association Object.

Thực tế, nếu các lớp Tablet hoặc Correspondent không xác định phương thức __init__ hoặc không chuyển bất kỳ tham số nào, giải pháp không hoạt động (không có đối số dự kiến).

Tôi đã tìm thấy giải pháp thay thế. Thật dễ dàng để phát hiện lớp nào phải được ủy quyền, vì vậy nó có thể được gán cho trường phù hợp (và vẫn làm việc để thêm các liên kết khác):

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

Cách tốt nhất để tránh sử dụng lambdas. Cảm ơn! –

Các vấn đề liên quan