2013-07-19 13 views
10

Jestem nowy w użyciu sqlalchemy. Jak pozbyć się błędnego błędu zależności dla tabel przedstawionych poniżej. Zasadniczo moim celem jest stworzenie tabeli pytań z jednoosobową relacją "najlepsza odpowiedź", aby odpowiedzieć, a także jedną z wielu relacji "możliwe".Jak pozbyć się błędu zależności cyklicznej podczas tworzenia bazy danych w 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 

To jest komunikat o błędzie: CircularDependencyError: Circular zależność wykryte. Cykle:

+0

Czy możesz podać więcej informacji o błędzie, jeśli to możliwe? Dlaczego masz identyfikator pytania dziecka i rodzica? Czy możesz spróbować to zrobić, ponieważ twoje pytanie nie wyjaśnia tego wymogu? –

Odpowiedz

6

Podobno SQLAlchemy nie działa dobrze w przypadku zależności cyklicznych. Można rozważyć zastosowanie tabelę stowarzyszenie zamiast reprezentować najlepszą odpowiedź ...

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

Dokładnie to zrobiłem. –

0

Można też rodzaj „ozdobić” swoje modele raz oni wstępnie zdefiniowane.

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) 

To nie jest zbyt miłe, ponieważ definicja klasy zaczyna się trochę unosić, ale to wystarczy.

1

Marka rozwiązanie działa, ale chciałem znaleźć sposób, aby to zrobić bez tworzenia dodatkowej tabeli. Po szeroko zakrojonych poszukiwań końcu stwierdzono tego przykładu w Dokumentach

http://docs.sqlalchemy.org/en/latest/orm/relationship_persistence.html (2nd przykład)

Podejście jest użycie primaryjoin [1] w obu związków w modelu Question, i dodanie post_update=True na jednym z nich. post_update mówi sqlalchemy, aby ustawić best_answer_id jako dodatkową instrukcję UPDATE, omijając okrągłą zależność.

Potrzebujesz również foreign_keys określonego w relacji question w modelu Answer.

Poniżej znajduje się kod Mark'a zmodyfikowany w celu podążenia za połączonym przykładem powyżej. Przetestowałem to z 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] Co ciekawe, „format string” dla primaryjoin wydaje się powodować błąd - ale konstruowania wyrażenia SQL z przeciążonych operatorów na kolumnie obiektów dzieł.

Powiązane problemy