2011-02-12 16 views
7

Tengo un modelo de base de datos en el que necesito una relación uno a varios y dos relaciones uno a uno. Aquí está el modelo que he hecho, pero está lanzando erroresRelaciones autorreferenciales múltiples en 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 

me sale el siguiente error siempre que intente hacer nada para la base de datos

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. 

Lo que es realmente raro es que haya funcionado sin el próximo y columnas previas. ¿Alguien sabe lo que está mal?

Respuesta

14

El tema es antiguo, pero como esto es tan confuso lo escribiré.
No necesita una columna 'prev' separada, ya la tiene como reverso para 'siguiente'. También, ya que tiene varias claves ajenas a la misma meta, es necesario especificar primaria se une de forma manual:

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 par de errores o sólo trozos de comportamiento extraño me di cuenta:
- sólo se puede use remote_side="Page.id", no remote_side=[id] y no remote_side=["Page.id"], o no funcionará (sqlalchemy 0.6.6). Esto era molesto de precisar.
- Parece que siempre debe usar remote_side con clave principal, independientemente de cuál sea su lado remoto real. remote_side="Pages.next_id" siempre generará un error extraño, incluso si parece apropiado.
- la expresión primaryjoin es confusa porque no usa alias, pero esta es la forma correcta de hacerlo. El motor de enlace sabe qué expresión reemplazar con un parámetro (que es demasiado implícito y está en contra del Zen, por cierto).

1

Puede utilizar 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)) 
Cuestiones relacionadas