Using Flask-Marshmallow for easy JSON Serialization

brythewiseguy

Bry

Posted on October 9, 2023

Using Flask-Marshmallow for easy JSON Serialization

I think it's safe to say everyone likes to make things in their lives just a little bit easier. During the past two weeks, I've been developing a full stack application that utilizes the Flask framework for Python and SQLAlchemy as it's backend. When I first started to think about my model structures, I was curious how I was going to control what these would look like when transformed into the JSON used by my frontend. I tried to use Serializer Mixin, but with so many interconnected bi-directional relationships the need for multiple serialization rules just made things really messy.

In Comes Flask-Marshmallow!!

Flask-Marshmallow made things much more simple, clean, and easy to understand. It's going to be my go-to serializer for the time being, simply because of how easy it made controlling what my JSON responses included and how it was integrated into my Flask routes. Today I am here to take you through a semi-brief walkthrough on the in's and out's of creating schema's for two models and a simple join table, and how we utilize Flask-Marshmallow to shape our JSON responses to frontend requests. I will not be going over the specifics on how to set up the models or the join table - I have a separate blog post HERE that goes through the steps to create models and a join table with SQLAlchemy.

Getting Started

First things first: Obviously we are working with Python and Flask, but we will need to install the necessary dependencies in order to use Flask-Marshmallow. Since I am using SQLAlchemy to define my models, I installed both Flask-Marshmallow and Marshmallow-SQLAlchemy. Using a Python Virtual Environment, you can run the following command to install both simultaneously:

pipenv install Flask-Marshmallow SQLAlchemy-Marshmallow

Once these are installed, there is an import and variable setup we need to utilize Marshmallow in our app. Wherever you decide to define your schema's, you will need to do two things:

  1. from flask_marshmallow import Marshmallow - This will import the Marshmallow class that will help Marshmallow integrate with Flask

  2. ma = Marshmallow(app) - This sets up the Marshmallow instance for your Flask app. The ma variable will be used to setup our schema's! Be sure you import your app to be used here!

Dependency Imports

The Models

For this example, I will be using 3 models: a User, a Game, and a UserLibrary model. I'm not going to go into depth on these - all 3 are relatively straight forward SQLAlchemy models that are similar to the models I outline in my previous blog post:

User Model

Game Model

UserLibrary Model

The Schema's

The coolest part about Flask-Marshmallow for me was setting up my schema's. The schema's offer a pretty intuitive way to self define what we send to our frontend through JSON serialization. This makes it really easy to manage things like sensitive user data, or data that only needs to be utilized on the backend of your application. The best part is that they are defined very similar to how a SQLAlchemy model is defined, with some obvious differences. I'm going to breakdown my User_Schema as an example:

User_Schema

In our user schema, we have defined all of the data we want to send back to the frontend side of our application about the user. We could change this if, for example, we did not want to send back the user's email to the frontend. Maybe we only want that data to be privately stored on the backend - it's as easy as not including it in your schema.

We define our schema, passing in ma.SQLAlchemySchema as an argument - this tells Marshmallow that it should inherit from a SQLAlchemy model. The User_Schema then defines a class instance Meta that is linked to our User model. This is used to associate the schema with the model. We then define all of the data we want included in the JSON response. ma.auto_field() is a quick and easy way to define the data types of each attribute - it allows Marshmallow to automatically infer the field type based on the data type of the attribute in the schema. You'll notice for the library attribute, we are using a different attribute definition ma.Pluck. We'll get into this soon, don't worry!!

Below the schema we have defined two variables, each of which has a different use case for the schema instance. The singular_user_schema is used any time we want the response to be for a singular user. Alternatively, the multiple_user_schema is used whenever we need to return multiple users in our response - this is accomplished by passing many=true to our schema instance. My Game_Schema is very similar:

Game_Schema

The Association Schema

Now to the fun part!! I know you are pretty curious about that ma.Pluck attribute definition and this is where that comes into play. Below is our User_Library_Schema:

User_Library_Schema

This schema looks relatively simple. You'll notice one small difference though: the game attribute is defined with ma.Nested(singular_game_schema). This tells Marshmallow to go find our game schema, and nest that schema into our user library schema. If you recall, the UserLibrary model is a join table that specifically houses a game and a user - this is the schema that helps facilitate the relationship between the two. When we pull in data for each user's library, we want all of their games nested into a nicely packaged UserLibrary record on our backend.

Now going back to our User model and our User_Schema. In our User model, we established a bi-directional relationship with the UserLibrary model through the library attribute. Each user will have multiple library entries on our backend. library = ma.Pluck("User_Library_Schema", 'game', many=True) takes our library attribute, hops over to our User_Library_schema and "plucks" the game objects out of it for the associated user. It then nests those games inside of the library attribute that we are sending back with our user JSON data to the frontend. Here's a visual representation of what that response might look like:

JSON Response

As you can see, the serialization of the user data gets sent back a library that is an array containing all of the game objects associated with our user. Pretty dang cool if you ask me!!

Conclusion

Phew! This has been a long blog post, and I hope you've made it this far! Flask-Marshmallow is a really powerful tool that gives us a lot more control over our data serialization, and makes it a lot more intuitive and easier to understand when we are in the thick of creating a complex database structure. This honestly saved my life when I was working on my most recent project. I was so lost in complex serialization rules, I almost lost track of which rule needed to be defined where. I honestly highly advise everyone to check this tool out if you haven't already - I promise you it makes creating a Flask-SQLAlchemy backend a lot easier. Out with those pesky serialization rules!! I'm going to include my full repo for the related project below - please feel free to check it out, as I have a LOT more schema's than the one's I used as examples here. Happy Coding y'all!

GitHub Repo: https://github.com/BryTheWiseGuy/re-flex-games-app

💖 💪 🙅 🚩
brythewiseguy
Bry

Posted on October 9, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related