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:
from flask_marshmallow import Marshmallow
- This will import the Marshmallow class that will help Marshmallow integrate with Flaskma = Marshmallow(app)
- This sets up the Marshmallow instance for your Flask app. Thema
variable will be used to setup our schema's! Be sure you import yourapp
to be used here!
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:
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:
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:
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
:
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:
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
Top comments (1)
hello! Great article. I've basically understood how to use Marshmallow. Plus your previous article on tables. Need to make a flask API and not sure which libraries to use. Especially between Flask_restful and Flask_smorest