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.