Flask-Pydantic For Data Validation

Hello Folks,

This is My First Blog on Hash node. Hope you will enjoy the read.

Introduction Of Flask-Pydantic Extension

Before knowing about Flask-Pydantic Extension, I want you all to know about what is pydantic? pydantic package is used for data validation using Python type hints and Flask-Pydantic is Flask Extension for integration of the awesome pydantic package with Flask.

Suppose you are building blog API with Flask then there is a feature where the user can create a post and when the user is sending the data to the server then there is a particular format in which we want the user to send the data, for example, we want the user to send the title of the blog in string format and what if the user is sending the title of type integer this may cause the error, so we want to tell the user to send the data in a particular format and some times we don't want the user to send only few information and miss the other important information when user wants to create a post then our API has some required parameters which need to be passed by the user such as title, subtitle and content and we want the user to send all the data and missing any parameter will cause the failure of our app.

Now how we can put these restrictions such as we want the data in a particular type and some are required parameters mandatory to pass and some are optional how we can tell the user if they are missing some parameters without our application crashing?

Here is the answer we have the Flask Extension Flask-Pydantic that will help us to validate the data sent by the user and it will tell the user in the nice formatted message if they are missing some required parameters or type of parameters are not the same as our app is accepting.

Installation

pip install Flask-Pydantic

Here is the official doc link of Flask-Pydantic Extension.

Usage With Example

Suppose we are building blog API with Flask then we have the route that will create a post and the user will send the data in the format such as some required parameters like title, subtitle and content of the blog post of type string and there is one optional parameter like rating of type string it is optional the app will not crash if the user doesn't send the rating. So Here we will create a model name PostBody that will be used to validate the post data sent by the user.

from pydantic import BaseModel
from typing import Optional


class PostBody(BaseModel):
    title: str
    subtitle: str
    content: str
    rating: Optional[str]

To define the PostBody, first, we need to import the BaseModel from pydantic then we need to create a class name PostBody that will inherit the BaseModel of pydantic and then in this class, you can define the parameters and its type in code we have three required parameters title, subtitle, the content of type string and one optional parameter rating on type string to define it optional we need to import the optional from tying module.

Now to use this model we need to pass it as a parameter name body to the route of create-post

from flask import Flask
from pydantic import BaseModel
from typing import Optional
from flask_pydantic import validate

app = Flask(__name__)

all_posts = [{"title": "This is first title", "subtitle": "This is the first subtitle", "content": "This is content of the first blog"},]


class PostBody(BaseModel):
    title: str
    subtitle: str
    content: str
    rating: Optional[str]


@app.route("/", methods=["POST"])
@validate()
def create_post(body: PostBody):
    print(body)
    all_posts.append(body.dict())
    return "Success"

if __name__ == "__main__":
    app.run(debug=True)

Code Explanation

In the route first to validate the post body the user is sending you need the validate method import from flask_pydantic and simply use validate as a decorator just below the app.route decorator.

Pass the PostBody model as the parameter name body(don't change the name it will cause error).

Run this file and we will test this API route with the POSTMAN

Output

Defining the Response Model

Sometimes you want to send the response in a particular format and this is good you can save yourself from sending sensitive information in the response.

First Define the Response Model

from pydantic import BaseModel

class PostReponse(BaseModel):
    title: str
    subtitle: str
    content: str

Here we created a class name PostResponse and it is inheriting the BaseModel of pydantic

Use the Response Model in our API

from flask import Flask
from pydantic import BaseModel
from typing import Optional
from flask_pydantic import validate


app = Flask(__name__)


all_posts = [{"title": "This is first title", "subtitle": "This is the first subtitle", "content": "This is content of the first blog"},
             ]


class PostBody(BaseModel):
    title: str
    subtitle: str
    content: str
    rating: Optional[str]


class PostReponse(BaseModel):
    title: str
    subtitle: str
    content: str

@app.route("/", methods=["POST"])
@validate()
def create_post(body: PostBody):
    print(body)
    all_posts.append(body.dict())
    return PostReponse(
        title=body.title,
        subtitle=body.subtitle,
        content=body.content
    )

if __name__ == "__main__":
    app.run(debug=True)

Code Explanation

Instead of sending Success here, we are sending the PostResponse object with the fields title, subtitle, and content.

Testing with POSTMAN

Testing with content field missing

Validating the query parameter

Suppose you want to get one post with the post_id then you want to validate if the user is providing post_id query parameter with the correct type then first, define the PostQuery model

from pydantic import BaseModel

class PostQuery(BaseModel):
    post_id: int

Now pass this PostQuery model as a parameter name query in the get_post method

@app.route('/post')
@validate()
def get_post(query: PostQuery):
    post_id = query.post_id
    requested_post = all_posts[post_id]
    return PostReponse(
        title=requested_post['title'],
        subtitle=requested_post['subtitle'],
        content=requested_post['content']
    )

Testing this API route with POSTMAN

Testing For Incorrect id

Complete Code

from flask import Flask
from pydantic import BaseModel
from typing import Optional
from flask_pydantic import validate


app = Flask(__name__)


all_posts = [{"title": "This is first title", "subtitle": "This is the first subtitle", "content": "This is content of the first blog"},
             ]


class PostBody(BaseModel):
    title: str
    subtitle: str
    content: str
    rating: Optional[str]


class PostReponse(BaseModel):
    title: str
    subtitle: str
    content: str


class PostQuery(BaseModel):
    post_id: int


@app.route("/", methods=["POST"])
@validate()
def create_post(body: PostBody):
    print(body)
    all_posts.append(body.dict())
    return PostReponse(
        title=body.title,
        subtitle=body.subtitle,
        content=body.content
    )


@app.route('/post')
@validate()
def get_post(query: PostQuery):
    post_id = query.post_id
    requested_post = all_posts[post_id]
    return PostReponse(
        title=requested_post['title'],
        subtitle=requested_post['subtitle'],
        content=requested_post['content']
    )


if __name__ == "__main__":
    app.run(debug=True)

Thanks for Reading.

Open for Feedback.