How Does Flask Marshmallow Schema Introspection of Database Models Work?

304 views Asked by At

I am trying to understand how the the introspection of Marshmallow Schemas works. Here's a simple code example:

import os

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from pprint import pprint


db_file = "database.db"
if os.path.isfile(db_file):
    os.remove(db_file)

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_file}"
app.app_context().push()

db = SQLAlchemy(app)
ma = Marshmallow(app)


class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    age = db.Column(db.Integer)


class MySchema(ma.Schema):
    class Meta:
        model = Person
        sqla_session = db.session
    

db.create_all()

alice = Person(name="Alice", age=25)
bob = Person(name="Bob", age=26)

db.session.add(alice)
db.session.add(bob)
db.session.commit()

persons = Person.query.all()
schema = MySchema(many=True)
result = schema.dump(persons)
pprint(result)

When I run this script the result I get is [{}, {}]. This tells me it does retrieve the Person objects from the database, but the Schema has no fields, so it just returns empty dicts. I expect this script to return [{"id": 0, "name": "Alice", "age": 25}, {"id": 1, "name": "Bob", "age": 26}], or something similar. Clearly the introspection of the database model has not happened. Why not? How should I modify this script to get the expected result?

For completeness, I am using Python 3.8.5 and the result of a pip freeze on my environment is this:

alembic==1.4.3
aniso8601==8.0.0
attrs==20.2.0
click==7.1.2
Flask==1.1.2
flask-marshmallow==0.14.0
Flask-Migrate==2.5.3
flask-restx==0.2.0
Flask-Script==2.0.6
Flask-SQLAlchemy==2.4.4
itsdangerous==1.1.0
Jinja2==2.11.2
jsonschema==3.2.0
Mako==1.1.3
MarkupSafe==1.1.1
marshmallow==3.8.0
marshmallow-sqlalchemy==0.23.1
psycopg2-binary==2.8.6
pyrsistent==0.17.3
python-dateutil==2.8.1
python-editor==1.0.4
pytz==2020.1
six==1.15.0
SQLAlchemy==1.3.20
Werkzeug==0.16.1

Note: I know I can get this to work by modifying my MySchema class like this:

class MySchema(ma.SQLAlchemySchema):
    class Meta:
        model = Person
        sqla_session = db.session
    
    id = ma.auto_field()
    name = ma.auto_field()
    age = ma.auto_field()

But this defies the purpose of having complete auto introspection. I want to avoid having to define the fields in two places.

1

There are 1 answers

0
D. de Vries On

Thanks to the comment by pjcunningham I managed to get this to work. Like he mentioned, all that had to be done was to replace ma.SQLAlchemySchema with ma.SQLAlchemyAutoSchema. After this change, the script returned the desired result.