Flask extension for integration with Pydantic library.
Project description
Flask-Pydantic
Flask extension for integration of the awesome pydantic package with Flask.
Pallets Community Ecosystem
[!IMPORTANT]
This project is part of the Pallets Community Ecosystem. Pallets is the open source organization that maintains Flask; Pallets-Eco enables community maintenance of Flask extensions. If you are interested in helping maintain this project, please reach out on the Pallets Discord server.
Installation
python3 -m pip install Flask-Pydantic
Basics
URL query and body parameters
validate decorator validates query, body and form-data request parameters and makes them accessible two ways:
| parameter type | request attribute name |
|---|---|
| query | query_params |
| body | body_params |
| form | form_params |
URL path parameter
If you use annotated path URL path parameters as follows
@app.route("/users/<user_id>", methods=["GET"])
@validate()
def get_user(user_id: str):
pass
flask_pydantic will parse and validate user_id variable in the same manner as for body and query parameters.
Additional validate arguments
- Success response status code can be modified via
on_success_statusparameter ofvalidatedecorator. response_manyparameter set toTrueenables serialization of multiple models (route function should therefore return iterable of models).request_body_manyparameter set toFalseanalogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects400response is returned,get_json_params- parameters to be passed toflask.Request.get_jsonfunction- If validation fails,
400response is returned with failure explanation.
For more details see in-code docstring or example app.
Usage
Example 1: Query parameters only
Simply use validate decorator on route function.
:exclamation: Be aware that @app.route decorator must precede @validate (i. e. @validate must be closer to the function declaration).
from typing import Optional
from flask import Flask, request
from pydantic import BaseModel
from flask_pydantic import validate
app = Flask("flask_pydantic_app")
class QueryModel(BaseModel):
age: int
class ResponseModel(BaseModel):
id: int
age: int
name: str
nickname: Optional[str] = None
# Example 1: query parameters only
@app.route("/", methods=["GET"])
@validate()
def get(query: QueryModel):
age = query.age
return ResponseModel(
age=age,
id=0, name="abc", nickname="123"
)
See the full example app here
agequery parameter is a requiredintcurl --location --request GET 'http://127.0.0.1:5000/'- if none is provided the response contains:
{ "validation_error": { "query_params": [ { "loc": ["age"], "msg": "field required", "type": "value_error.missing" } ] } }
- for incompatible type (e. g. string
/?age=not_a_number) curl --location --request GET 'http://127.0.0.1:5000/?age=abc'{ "validation_error": { "query_params": [ { "loc": ["age"], "msg": "value is not a valid integer", "type": "type_error.integer" } ] } }
- likewise for body parameters
- example call with valid parameters:
curl --location --request GET 'http://127.0.0.1:5000/?age=20'
-> {"id": 0, "age": 20, "name": "abc", "nickname": "123"}
Example 2: URL path parameter
# Example 2: URL path parameter only
@app.route("/character/<character_id>/", methods=["GET"])
@validate()
def get_character(character_id: int):
characters = [
ResponseModel(id=1, age=95, name="Geralt", nickname="White Wolf"),
ResponseModel(id=2, age=45, name="Triss Merigold", nickname="sorceress"),
ResponseModel(id=3, age=42, name="Julian Alfred Pankratz", nickname="Jaskier"),
ResponseModel(id=4, age=101, name="Yennefer", nickname="Yenn"),
]
try:
return characters[character_id]
except IndexError:
return {"error": "Not found"}, 400
Example 3: Request body only
class RequestBodyModel(BaseModel):
name: str
nickname: Optional[str] = None
# Example 3: request body only
@app.route("/", methods=["POST"])
@validate()
def post(body: RequestBodyModel):
name = body.name
nickname = body.nickname
return ResponseModel(
name=name, nickname=nickname,id=0, age=1000
)
See the full example app here
Example 4: BOTH query paramaters and request body
# Example 4: both query paramters and request body
@app.route("/both", methods=["POST"])
@validate()
def get_and_post(body: RequestBodyModel, query: QueryModel):
name = body.name # From request body
nickname = body.nickname # From request body
age = query.age # from query parameters
return ResponseModel(
age=age, name=name, nickname=nickname,
id=0
)
See the full example app here
Example 5: Request form-data only
class RequestFormDataModel(BaseModel):
name: str
nickname: Optional[str] = None
# Example 5: request form only
@app.route("/", methods=["POST"])
@validate()
def post(form: RequestFormDataModel):
name = form.name
nickname = form.nickname
return ResponseModel(
name=name, nickname=nickname,id=0, age=1000
)
See the full example app here
Modify response status code
The default success status code is 200. It can be modified in two ways
- in return statement
# necessary imports, app and models definition
...
@app.route("/", methods=["POST"])
@validate(body=BodyModel, query=QueryModel)
def post():
return ResponseModel(
id=id_,
age=request.query_params.age,
name=request.body_params.name,
nickname=request.body_params.nickname,
), 201
- in
validatedecorator
@app.route("/", methods=["POST"])
@validate(body=BodyModel, query=QueryModel, on_success_status=201)
def post():
...
Status code in case of validation error can be modified using FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE flask configuration variable.
Using the decorated function kwargs
Instead of passing body and query to validate, it is possible to directly
defined them by using type hinting in the decorated function.
# necessary imports, app and models definition
...
@app.route("/", methods=["POST"])
@validate()
def post(body: BodyModel, query: QueryModel):
return ResponseModel(
id=id_,
age=query.age,
name=body.name,
nickname=body.nickname,
)
This way, the parsed data will be directly available in body and query.
Furthermore, your IDE will be able to correctly type them.
Model aliases
Pydantic's alias feature is natively supported for query and body models. To use aliases in response modify response model
def modify_key(text: str) -> str:
# do whatever you want with model keys
return text
class MyModel(BaseModel):
...
model_config = ConfigDict(
alias_generator=modify_key,
populate_by_name=True
)
and set response_by_alias=True in validate decorator
@app.route(...)
@validate(response_by_alias=True)
def my_route():
...
return MyModel(...)
Example app
For more complete examples see example application.
Configuration
The behaviour can be configured using flask's application config
FLASK_PYDANTIC_VALIDATION_ERROR_STATUS_CODE - response status code after validation error (defaults to 400)
Additionally, you can set FLASK_PYDANTIC_VALIDATION_ERROR_RAISE to True to cause
flask_pydantic.ValidationError to be raised with either body_params,
form_params, path_params, or query_params set as a list of error
dictionaries. You can use flask.Flask.register_error_handler to catch that
exception and fully customize the output response for a validation error.
Contributing
Feature requests and pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
- clone repository
git clone https://github.com/pallets-eco/flask_pydantic.git cd flask_pydantic
- create virtual environment and activate it
python3 -m venv venv source venv/bin/activate
- install runtime dependencies
python3 -m pip install -e .
- install development requirements
python3 -m pip install -r requirements/test.txt
- checkout new branch and make your desired changes (don't forget to update tests)
git checkout -b <your_branch_name>
- make sure your code style is compliant with Ruff. Your can check these errors and automatically correct some of them with
ruff check --select I --fix . - run tests and check code format
python3 -m pytest --ruff --ruff-format
- push your changes and create a pull request to master branch
TODOs:
- header request parameters
- cookie request parameters
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flask_pydantic-0.14.0.tar.gz.
File metadata
- Download URL: flask_pydantic-0.14.0.tar.gz
- Upload date:
- Size: 12.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb27fa6fd62d952b421df05c72349b5b1dc6fadacbd52879aac80cc519e4eab6
|
|
| MD5 |
93f461e3bfc39fa52eb3de9574b075a8
|
|
| BLAKE2b-256 |
038b05c91742c72740fb98e18bc4b0a7645e5df4c0ef9fec868e7343aaea0504
|
Provenance
The following attestation bundles were made for flask_pydantic-0.14.0.tar.gz:
Publisher:
publish.yaml on pallets-eco/flask-pydantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_pydantic-0.14.0.tar.gz -
Subject digest:
eb27fa6fd62d952b421df05c72349b5b1dc6fadacbd52879aac80cc519e4eab6 - Sigstore transparency entry: 774873523
- Sigstore integration time:
-
Permalink:
pallets-eco/flask-pydantic@bc3fee46e680ce51bc8c32c71b3844dcd18bfd88 -
Branch / Tag:
refs/tags/v0.14.0 - Owner: https://github.com/pallets-eco
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@bc3fee46e680ce51bc8c32c71b3844dcd18bfd88 -
Trigger Event:
push
-
Statement type:
File details
Details for the file flask_pydantic-0.14.0-py3-none-any.whl.
File metadata
- Download URL: flask_pydantic-0.14.0-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82a5914d90983f21976c39f97ea2901eba54536ac4b24850e9d52a0676448301
|
|
| MD5 |
ea4cc7946d43d01fe7eb073baf48bec2
|
|
| BLAKE2b-256 |
7760cab2b0005e076875d869f74dd452cfd81b82a926c52bfff49c401bba0880
|
Provenance
The following attestation bundles were made for flask_pydantic-0.14.0-py3-none-any.whl:
Publisher:
publish.yaml on pallets-eco/flask-pydantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_pydantic-0.14.0-py3-none-any.whl -
Subject digest:
82a5914d90983f21976c39f97ea2901eba54536ac4b24850e9d52a0676448301 - Sigstore transparency entry: 774873527
- Sigstore integration time:
-
Permalink:
pallets-eco/flask-pydantic@bc3fee46e680ce51bc8c32c71b3844dcd18bfd88 -
Branch / Tag:
refs/tags/v0.14.0 - Owner: https://github.com/pallets-eco
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@bc3fee46e680ce51bc8c32c71b3844dcd18bfd88 -
Trigger Event:
push
-
Statement type: