1

I am trying to get flask-marshmallow to give me a list of translated entries from a Table called Type that is associated to a table called Language. I cannot figure out how to nest the responses in a list. I gave an example of my current output JSON along with my goal JSON.

class TypeSchema(ma.SQLAlchemySchema):
    class Meta:
        model = Type
        ordered = True
    language = ma.String(attribute="language.language", dump_only=True)
    translated_languages = ma.List(ma.String(attribute="language"))

class Language(Updateable, db.Model):
    __tablename__ = 'language'

    id = sqla.Column(sqla.Integer, primary_key=True)
    language = sqla.Column(sqla.String(2), nullable=False)

    type_id = sqla.Column(sqla.Integer, sqla.ForeignKey('type.id'), index=True)
    type = sqla_orm.relationship('Type', foreign_keys='Language.type_id', back_populates='translated_languages')

    types_id = sqla.Column(sqla.Integer, sqla.ForeignKey('type.id'), index=True)
    types = sqla_orm.relationship('Type', foreign_keys='Language.types_id', back_populates='language')


class Type(Updateable, db.Model):
    __tablename__ = 'type'

    id = sqla.Column(sqla.Integer, primary_key=True)

    translated_languages = sqla_orm.relationship('Language', back_populates='type', foreign_keys='Language.type_id')
    language = sqla_orm.relationship('Language', back_populates='types', uselist=False, foreign_keys='Language.types_id')

Here is the resulting JSON

    {
      "id": 1,
      "translated_languages": [
        "<api.models.Language object at 0x00000171D3730490>", 
        "<api.models.Language object at 0x00000171D3730400>", 
        "<api.models.Language object at 0x00000171D3730520>"
      ], 
      "language": "en", 
    }

Here is my goal JSON

    {
      "id": 1,
      "translated_languages": [
        "fr", 
        "es", 
        "de"
      ], 
      "language": "en", 
    }
rockets4all
  • 684
  • 3
  • 8
  • 32

1 Answers1

2

You can do this with a combination of marshmallow, marshmallow-sqlalchemy and flask-marshmallow. Nested objects are usually serialised as dictionaries to enable easy round-tripping of the data. Since you want simple strings rather than dicts we can use marshmallow's post_dump decorator to massage the data after it has been generated.

import pprint as pp

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import marshmallow as mm
import marshmallow_sqlalchemy as ms
import flask_marshmallow as ma

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
db = SQLAlchemy(app)

# Model definitions omitted.


class LanguageSchema(ma.sqla.SQLAlchemyAutoSchema):
    class Meta:
        model = Language
    fields = ('language',)


class TypeSchema(ma.sqla.SQLAlchemyAutoSchema):
    class Meta:
        model = Type
        include_relationships = True

    language = ma.fields.fields.String(attribute='language.language', dump_only=True)
    translated_languages = ms.fields.Nested(
        LanguageSchema, many=True, exclude=('id',)
    )

    @mm.post_dump
    def patch_translated_languages(self, data, many, **kwargs):
        """Represent translated languages as simple strings."""
        language_names = [d['language'] for d in data['translated_languages']]
        data['translated_languages'] = language_names
        return data

# Flask 3.x requires app_context.
with app.app_context():
    db.create_all()
    de, en, es, fr = [
        Language(language=lang) for lang in ['de', 'en', 'es', 'fr']
    ]
    type_ = Type(language=en, translated_languages=[fr, es, de])
    db.session.add(type_)
    db.session.commit()

with app.app_context():
    type_ = Type.query.first()
    dump = TypeSchema().dump(type_)
    pp.pprint(dump)

It wasn't clear to me what your imports were, so I've included mine for clarity.

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153
  • Thanks for the help! Enjoy the bounty. By any chance do you have any tips on how I could have worded this question to get more traction without a bounty? – rockets4all Oct 08 '22 at 14:35
  • I'm not sure what wording would have improved the question. It may just be that there aren't many people who know marshmallow in depth, or that want to learn more about, and happen to answer questions on SO. Also what you were trying to do is unusual (because it doesn't round-trip easily), so not many folks will know how to do it without doing some research. – snakecharmerb Oct 08 '22 at 14:50
  • Incidentally you haven't actually awarded the bounty, you have only accepted the answer. But if I were you I would wait until it has expired and award it then. You never know, someone may come along with a better answer in the meantime. – snakecharmerb Oct 08 '22 at 14:52
  • Thanks again for all the help. I am trying to learn Marshmallow in order to make a backend for a front-end template I bought in order to learn react. It came with a nodeJS fake backend which I am trying to replicate in Marshmallow. Do you offer any paid help? It would be nice to have a mentor to help me learn this faster. – rockets4all Oct 08 '22 at 16:25