2013-07-19 13 views
10

Sono nuovo nell'utilizzo di sqlalchemy. Come faccio a eliminare un errore di dipendenza circolare per le tabelle mostrate di seguito. Fondamentalmente il mio obiettivo è quello di creare una tabella di domande con una relazione "risposta migliore" uno a uno e una relazione uno a molti "possibili_answer".Come si elimina un errore di dipendenza circolare durante la creazione di un database in sqlalchemy?

class Answer(Base): 
    __tablename__ = 'answers' 
    id = Column(Integer, primary_key=True) 
    text = Column(String) 

    question_id = Column(Integer, ForeignKey('questions.id')) 

    def __init__(self, text, question_id): 
     self.text = text 

    def __repr__(self): 
     return "<Answer '%s'>" % self.text 

class Question(Base): 
    __tablename__ = 'questions' 

    id = Column(Integer, primary_key=True) 
    text = Column(String) 
    picture = Column(String) 
    depth = Column(Integer) 
    amount_of_tasks = Column(Integer) 
    voting_threshold = Column(Integer) 
    best_answer_id = Column(Integer, ForeignKey('answers.id'), nullable=True) 

    possible_answers = relationship("Answer", post_update=True, primaryjoin = id==Answer.question_id) 

    def __init__(self, text, picture, depth, amount_of_tasks): 
     self.text = text 
     self.picture = picture 
     self.depth = depth 
     self.amount_of_tasks = amount_of_tasks 

    def __repr__(self): 
     return "<Question, '%s', '%s', '%s', '%s'>" % (self.text, self.picture, self.depth, self.amount_of_tasks) 

    def __repr__(self): 
     return "<Answer '%s'>" % self.text 

Questo è il messaggio di errore: CircularDependencyError: Dipendenza circolare rilevato. Cicli:

+0

È possibile includere più messaggi di errore se possibile? Perché si dispone di ID domanda figlio e genitore? Puoi provare a farlo perché la tua domanda non spiega questo requisito? –

risposta

6

Apparentemente SQLAlchemy non funziona bene con le dipendenze circolari. Si potrebbe considerare l'utilizzo di una tabella di associazione invece a rappresentare la migliore risposta ...

from sqlalchemy import Column, Integer, String, ForeignKey, create_engine 
from sqlalchemy import Table 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import relationship, sessionmaker 

engine = create_engine('sqlite:///:memory:') 
Base = declarative_base() 


class Answer(Base): 
    __tablename__ = 'answer' 

    id = Column(Integer, primary_key=True) 
    question_id = Column(Integer, ForeignKey('question.id')) 
    text = Column(String) 

    question = relationship('Question', backref='answers') 

    def __repr__(self): 
     return "<Answer '%s'>" % self.text 


class Question(Base): 
    __tablename__ = 'question' 

    id = Column(Integer, primary_key=True) 
    text = Column(String) 

    best_answer = relationship('Answer', 
           secondary=lambda: best_answer, 
           uselist=False) 

    def __repr__(self): 
     return "<Question, '%s'>" % (self.text) 

best_answer = Table('best_answer', Base.metadata, 
        Column('question_id', 
          Integer, 
          ForeignKey('question.id'), 
          primary_key=True), 
        Column('answer_id', 
          Integer, 
          ForeignKey('answer.id')) 
        ) 


if __name__ == '__main__': 

    session = sessionmaker(bind=engine)() 
    Base.metadata.create_all(engine) 

    question = Question(text='How good is SQLAlchemy?') 

    somewhat = Answer(text='Somewhat good') 
    very = Answer(text='Very good') 
    excellent = Answer(text='Excellent!') 

    question.answers.extend([somewhat, very, excellent]) 
    question.best_answer = excellent 

    session.add(question) 
    session.commit() 

    question = session.query(Question).first() 

    print(question.answers) 
    print(question.best_answer) 
+0

Questo è esattamente quello che ho finito per fare. –

0

Si potrebbe anche sorta di 'arredare' i vostri modelli una volta che sono inizialmente definiti.

class Answer(Base): 
     __tablename__ = 'answers' 
     id = Column(Integer, primary_key=True) 
     text = Column(String) 

    class Question(Base): 
     __tablename__ = 'questions' 

     id = Column(Integer, primary_key=True) 
     text = Column(String) 
     picture = Column(String) 
     depth = Column(Integer) 
     amount_of_tasks = Column(Integer) 
     voting_threshold = Column(Integer) 
     best_answer_id = Column(Integer, ForeignKey('answers.id'), nullable=True) 

    Answer.question_id = Column(Integer, ForeignKey(Question.id)) 
    Question.possible_answers = relationship(Answer, post_update=True, primaryjoin=Question.id==Answer.question_id) 

Non è bello come la definizione della classe inizia a fluttuare un po 'ma fa il trucco.

1

soluzione di Mark funziona, ma volevo trovare un modo per farlo senza creare una tabella aggiuntiva. Dopo una lunga ricerca, ho finalmente trovato questo esempio nella documentazione:

http://docs.sqlalchemy.org/en/latest/orm/relationship_persistence.html (2 ° esempio)

L'approccio è quello di utilizzare primaryjoin [1] su entrambi i rapporti nel modello Question, e per aggiungere post_update=True su un di loro. Il post_update dice a sqlalchemy di impostare best_answer_id come un'ulteriore istruzione UPDATE, aggirando la dipendenza circolare.

È inoltre necessario specificare foreign_keys nella relazione question nel modello Answer.

Di seguito è stato modificato il codice di Mark per seguire l'esempio collegato sopra. L'ho provato con sqlalchemy v1.1.9.

from sqlalchemy import Column, Integer, String, ForeignKey, create_engine 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import relationship, sessionmaker 

engine = create_engine('sqlite:///:memory:') 
Base = declarative_base() 

class Answer(Base): 
    __tablename__ = 'answer' 
    id = Column(Integer, primary_key=True) 
    text = Column(String) 
    question_id = Column(Integer, ForeignKey('question.id')) 
    question = relationship('Question', back_populates='answers', foreign_keys=[question_id]) 

    def __repr__(self): 
     return "<Answer '%s'>" % self.text 

class Question(Base): 
    __tablename__ = 'question' 
    id = Column(Integer, primary_key=True) 
    text = Column(String) 
    best_answer_id = Column(Integer, ForeignKey('answer.id')) 
    answers  = relationship('Answer', primaryjoin= id==Answer.question_id) 
    best_answer = relationship('Answer', primaryjoin= best_answer_id==Answer.id, post_update=True) 

    def __repr__(self): 
     return "<Question, '%s'>" % (self.text) 

if __name__ == '__main__': 

    session = sessionmaker(bind=engine)() 
    Base.metadata.create_all(engine) 

    question = Question(text='How good is SQLAlchemy?') 

    somewhat = Answer(text='Somewhat good') 
    very = Answer(text='Very good') 
    excellent = Answer(text='Excellent!') 

    question.answers.extend([somewhat, very, excellent]) 
    question.best_answer = excellent 

    session.add(question) 
    session.commit() 

    question = session.query(Question).first() 

    print(question.answers) 
    print(question.best_answer) 

[1] È interessante notare che il "format string" per primaryjoin sembra causare un errore - ma costruire l'espressione SQL con gli operatori di overload sulla colonna di oggetti opere.

Problemi correlati