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",
}
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.