2011-02-12 14 views
7

Ho un modello di database in cui ho bisogno di una relazione uno-a-molti e due relazioni uno-a-uno. Ecco il modello che ho fatto, ma è gettando erroriRelazioni autoreferenziali multiple in SQLAlchemy

class Page(Base): 
    __tablename__ = 'pages' 
    id   = Column(Integer, primary_key=True) 
    title  = Column(String(100), nullable=False) 
    content  = Column(Text, nullable=False) 

    parent_id = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    children = relationship("Page", backref=backref("parent", remote_side=id)) 

    next_id  = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    next  = relationship("Page", backref=backref("prev", remote_side=id, uselist=False)) 

    prev_id  = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    prev  = relationship("Page", backref=backref("next", remote_side=id, uselist=False)) 

    def __init__(self, title, content, parent_id=None, next_id=None, prev_id=None): 
     self.title = title 
     self.content = content 
     self.parent_id = parent_id 
     self.next_id = next_id 
     self.prev_id = prev_id 

    def __repr__(self): 
     return '<Page "%r">' % self.title 

ottengo il seguente errore ogni volta che provo a fare qualcosa al database

ArgumentError: Could not determine join condition between parent/child tables on relationship Page.children. Specify a 'primaryjoin' expression. If 'secondary' is present, 'secondaryjoin' is needed as well. 

La cosa veramente strana è che ha funzionato senza il successivo e le colonne prev Qualcuno sa cosa c'è che non va?

risposta

14

L'argomento è vecchio, ma poiché questo è così confuso scriverò.
Non è necessario separare la colonna "prev", l'hai già come backref per "next". Inoltre, dal momento che si dispone di più chiavi esterne alla stessa destinazione, è necessario specificare primaria unisce manualmente:

class Page(Base): 
    __tablename__ = 'pages' 
    id   = Column(Integer, primary_key=True) 
    title  = Column(String(100), nullable=False) 
    content  = Column(Text, nullable=False) 

    parent_id = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    parent  = relationship("Page", 
        primaryjoin=('pages.c.id==pages.c.parent_id'), 
        remote_side='Page.id', 
        backref=backref("children")) 

    next_id  = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    next  = relationship("Page", 
        primaryjoin=('pages.c.next_id==pages.c.id'), 
        remote_side='Page.id', 
        backref=backref("prev", uselist=False)) 

un paio di bug o solo pezzi di comportamenti strani che ho notato:
- Si può solo utilizzare remote_side="Page.id", non remote_side=[id] e non remote_side=["Page.id"] oppure non funzionerà (sqlalchema 0.6.6). Questo era fastidioso da definire.
- Sembra che dovresti sempre usare remote_side con la chiave primaria, indipendentemente dal tuo lato remoto reale. remote_side="Pages.next_id" genererà sempre un errore strano, anche se sembra appropriato.
- l'espressione primaryjoin è un po 'confusa, poiché non usa alias, ma questo è in realtà il modo corretto di farlo. Il motore di associazione sa quale espressione sostituire con un parametro (che è troppo implicito e contro lo Zen, btw).

1

È possibile utilizzare foreign_keys:

class Page(Base): 
    __tablename__ = 'pages' 
    id   = Column(Integer, primary_key=True) 
    title  = Column(String(100), nullable=False) 
    content  = Column(Text, nullable=False) 

    parent_id = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    parent  = relationship("Page", 
        foreign_keys=[parent_id], 
        remote_side=[id], 
        backref=backref("children")) 

    next_id  = Column(Integer, ForeignKey("pages.id"), nullable=True) 
    next  = relationship("Page", 
        foreign_keys=[next_id], 
        remote_side=[id], 
        backref=backref("prev", uselist=False))