Search code examples
pythonsqlalchemyflask-sqlalchemymarshmallow

Related field not returned in marshmallow schema (also flask / sqlalchemy)


Using marshmallow 2.18.0, flask-marshmallow 0.9.0.

I'm building a system which handles a series of questions and answers. The next question displayed depends on the answer to the previous question. To model this I have two Answer foreign keys pointing to Question.

The problem is that AnswerSchema doesn't show the hyperlink or any data at all for next_question, which I'm trying to pull in using flask-marshmallow's HyperlinkRelated function. However it does work for question (the question that the answer is answering).

$ http localhost:5000/answers/1/
{
    "answer": "Great",
    "id": 1,
    "question": "/questions/1/", *# Works, but where is next_question?*
}

If it's relevant, I do see next_question in answer_schema._declared_fields, but not in answer_schema.dump(answer).data.

By the way, Nested works well in the other direction when I query for a Question:

$ http localhost:5000/questions/1/
{
    "answers": [
        {
            "answer": "Great",
            "id": 1,
            "question": "/questions/1/",
        },
        {
            "answer": "More than great",
            "id": 2,
            "question": "/questions/1/",
        }
    ],
    "id": 1,
    "question": "How are you doing today?",
}

Anyway, I'm not sure if HyperlinkRelated is the right way to go about this, but if it's not, I don't see what to do. I'd really appreciate understanding what is the correct approach (should I be using Nested in the other direction as well?!?!), and why i.e. what I've missed in the docs.

Here's related (I removed what I could to keep it short, it's a mix from different files):

from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from sqlalchemy_mixins import AllFeaturesMixin

db = SQLAlchemy()
ma = Marshmallow()

class GenericMixin:
    id = db.Column(db.Integer, primary_key=True)

class BaseModel(db.Model, AllFeaturesMixin, GenericMixin):
    __abstract__ = True

BaseModel.set_session(db.session)

from flask_classful import FlaskView
from webargs import fields
from webargs.flaskparser import use_kwargs

from .resources.user import User, User

class Question(BaseModel):
    question = db.Column(db.String(128), unique=True, nullable=False)
    answers = db.relationship('Answer', backref='question',
                              foreign_keys='Answer.question_id')

class QuestionSchema(ma.ModelSchema):
    class Meta:
        model = Question

    answers = ma.Nested(AnswerSchema, many=True, strict=True)

question_schema = QuestionSchema(strict=True)

class QuestionsView(FlaskView):
    def get(self, id):
        question = Question.query.get_or_404(id)
        return question_schema.jsonify(question)

class Answer(BaseModel):
    answer = db.Column(db.String(128), unique=True, nullable=False)
    question_id = db.Column(db.Integer,
                            db.ForeignKey('question.id'),
                            nullable=False)
    next_question_id = db.Column(db.Integer,
                                 db.ForeignKey('question.id'),
                                 nullable=True)

class AnswerSchema(ma.ModelSchema):
    class Meta:
        model = Answer

    question = ma.HyperlinkRelated('QuestionsView:get')
    # HELP! How do I get this to return the link to the next question?
    next_question = ma.HyperlinkRelated('QuestionsView:get')

answer_schema = AnswerSchema(strict=True)

class AnswersView(FlaskView): 
    def get(self, id):
        answer = Answer.query.get_or_404(id)
        return answer_schema.jsonify(answer)

Solution

  • Thanks to lftl on Reddit for providing the answer. I simply had to add the backref to Question.

    class Question(BaseModel):
        question = db.Column(db.String(128), unique=True, nullable=False)
        answers = db.relationship(
            "Answer", backref="question", foreign_keys="Answer.question_id"
        )
        next_question = db.relationship(
            "Answer", backref="next_question", foreign_keys="Answer.next_question_id"
        )
    

    The Reddit link contains other useful discussion. I found that HyperlinkRelated doesn't support foreign key nulls, but there's an open PR, and the monkey patch works great.