Reputation: 549
Please help me model this in SQLAlchemy. A user can create a question. A question can have any number of choices (E.g. YES, NO, LATER, MAY BE, DONT KNOW, NEXT YEAR, NOT APPLICABLE). I have created a mapper between questions and choices. How do I model the responses in SQLAlchemy?
question_choices = Table('question_choices', Base.metadata,
Column('id', Integer, primary_key=True),
Column('question_id', Integer, ForeignKey('questions.id')),
Column('choice_id', Integer, ForeignKey('choices.id'))
)
class Choice(Base):
__tablename__ = 'choices'
id = Column(Integer, primary_key=True)
value = Column(String(30), nullable=False)
class Question(Base):
__tablename__ = 'questions'
id = Column(Integer, primary_key=True)
title = Column(String(100))
created = Column(DateTime)
choices = relationship('Choice', secondary=question_choices)
class questionResponse(Base):
"""A friend's response to a question"""
__tablename__ = 'question_responses'
id = Column(Integer, primary_key=True)
question_id = Column(Integer, ForeignKey('questions.id'))
choice_id = Column(Integer, ForeignKey('choices.id'))
user_id = Column(Integer, ForeignKey('users.id'))
created = Column(DateTime)
The questionResponse model is not normalized. Question_id and listing_id are repeated. I do not have a relationship in the mapper table. I want to be able to count the responses for a given question.
Upvotes: 0
Views: 283
Reputation: 76992
Your mapper for QuestionResponse
is pretty good already, but it does not restrict an answer of the choice that is not configured: so if LATER
is not allowed answer for a question Will you marry me?
, the database does not restrict this.
One solution to this would be to also add a two-column foreign key constraint to the QuestionResponse
:
class QuestionResponse(Base):
"""A friend's response to a question"""
__tablename__ = 'question_responses'
id = Column(Integer, primary_key=True)
question_id = Column(Integer, ForeignKey('questions.id'))
choice_id = Column(Integer, ForeignKey('choices.id'))
# ...
__table_args__ = (
ForeignKeyConstraint(['question_id', 'choice_id'], ['question_choices.question_id', 'question_choices.choice_id']),
)
Alternative (more normalized DB model) is to define the FK only to the question_choices.id
:
class QuestionResponse(Base):
"""A friend's response to a question"""
__tablename__ = 'question_responses'
id = Column(Integer, primary_key=True)
question_choice_id = Column(Integer, ForeignKey('question_choices.id'))
edit-1: In this case you can have a relationship between Question and QuestionResponse defined like below, which will provide you with count as well:
class Question(Base):
# ....
answers = relationship("QuestionResponse",
primaryjoin="Question.id==question_choices.c.question_id",
secondary=question_choices,
secondaryjoin="question_choices.c.id==QuestionResponse.question_choice_id",
backref="question",
)
In any case you might want to add a UniqueConstraint
to the question_choices
table on columns (question_id, choice_id)
.
Now, in order to count responses, you either add the relationship between Question
and QuestionResponse
and return len(answers)
or you just create a query-based property on Question
:
class Question(Base):
# ...
answer_count = column_property(
select([func.count(QuestionResponse.__table__.c.id)]).
where(question_choices.c.question_id==id).
where(question_choices.c.id==QuestionResponse.__table__.c.question_choice_id)
)
Upvotes: 1