%global _empty_manifest_terminate_build 0
Name: python-Flask-Pydantic
Version: 0.11.0
Release: 1
Summary: Flask extension for integration with Pydantic library
License: MIT
URL: https://github.com/bauerji/flask_pydantic.git
Source0: https://mirrors.nju.edu.cn/pypi/web/packages/a8/f4/34f688e8a15c1efc7caede3cb9f81f9824e2daccd4b41436a28b2cb5c518/Flask-Pydantic-0.11.0.tar.gz
BuildArch: noarch
Requires: python3-Flask
Requires: python3-pydantic
%description
### Additional `validate` arguments
- Success response status code can be modified via `on_success_status` parameter of `validate` decorator.
- `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models).
- `request_body_many` parameter set to `False` analogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects `400` response is returned,
- `get_json_params` - parameters to be passed to [`flask.Request.get_json`](https://tedboy.github.io/flask/generated/generated/flask.Request.get_json.html) function
- If validation fails, `400` response 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.
```python
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]
# 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
- `age` query parameter is a required `int`
- `curl --location --request GET 'http://127.0.0.1:5000/'`
- if none is provided the response contains:
```json
{
"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'`
```json
{
"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
```python
@app.route("/character//", 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
```python
class RequestBodyModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: 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
```python
# Example 3: 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
```python
class RequestFormDataModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: request body 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
```python
# 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 `validate` decorator
```python
@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.
```python
# 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](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models.
To use aliases in response modify response model
```python
def modify_key(text: str) -> str:
# do whatever you want with model keys
return text
class MyModel(BaseModel):
class Config:
alias_generator = modify_key
allow_population_by_field_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](https://github.com/bauerji/flask_pydantic/tree/master/example_app).
### 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
```bash
git clone https://github.com/bauerji/flask_pydantic.git
cd flask_pydantic
```
- create virtual environment and activate it
```bash
python3 -m venv venv
source venv/bin/activate
```
- install development requirements
```bash
python3 -m pip install -r requirements/test.pip
```
- checkout new branch and make your desired changes (don't forget to update tests)
```bash
git checkout -b
```
- run tests
```bash
python3 -m pytest
```
- if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black)
- push your changes and create a pull request to master branch
## TODOs:
- header request parameters
- cookie request parameters
%package -n python3-Flask-Pydantic
Summary: Flask extension for integration with Pydantic library
Provides: python-Flask-Pydantic
BuildRequires: python3-devel
BuildRequires: python3-setuptools
BuildRequires: python3-pip
%description -n python3-Flask-Pydantic
### Additional `validate` arguments
- Success response status code can be modified via `on_success_status` parameter of `validate` decorator.
- `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models).
- `request_body_many` parameter set to `False` analogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects `400` response is returned,
- `get_json_params` - parameters to be passed to [`flask.Request.get_json`](https://tedboy.github.io/flask/generated/generated/flask.Request.get_json.html) function
- If validation fails, `400` response 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.
```python
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]
# 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
- `age` query parameter is a required `int`
- `curl --location --request GET 'http://127.0.0.1:5000/'`
- if none is provided the response contains:
```json
{
"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'`
```json
{
"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
```python
@app.route("/character//", 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
```python
class RequestBodyModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: 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
```python
# Example 3: 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
```python
class RequestFormDataModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: request body 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
```python
# 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 `validate` decorator
```python
@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.
```python
# 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](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models.
To use aliases in response modify response model
```python
def modify_key(text: str) -> str:
# do whatever you want with model keys
return text
class MyModel(BaseModel):
class Config:
alias_generator = modify_key
allow_population_by_field_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](https://github.com/bauerji/flask_pydantic/tree/master/example_app).
### 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
```bash
git clone https://github.com/bauerji/flask_pydantic.git
cd flask_pydantic
```
- create virtual environment and activate it
```bash
python3 -m venv venv
source venv/bin/activate
```
- install development requirements
```bash
python3 -m pip install -r requirements/test.pip
```
- checkout new branch and make your desired changes (don't forget to update tests)
```bash
git checkout -b
```
- run tests
```bash
python3 -m pytest
```
- if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black)
- push your changes and create a pull request to master branch
## TODOs:
- header request parameters
- cookie request parameters
%package help
Summary: Development documents and examples for Flask-Pydantic
Provides: python3-Flask-Pydantic-doc
%description help
### Additional `validate` arguments
- Success response status code can be modified via `on_success_status` parameter of `validate` decorator.
- `response_many` parameter set to `True` enables serialization of multiple models (route function should therefore return iterable of models).
- `request_body_many` parameter set to `False` analogically enables serialization of multiple models inside of the root level of request body. If the request body doesn't contain an array of objects `400` response is returned,
- `get_json_params` - parameters to be passed to [`flask.Request.get_json`](https://tedboy.github.io/flask/generated/generated/flask.Request.get_json.html) function
- If validation fails, `400` response 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.
```python
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]
# 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
- `age` query parameter is a required `int`
- `curl --location --request GET 'http://127.0.0.1:5000/'`
- if none is provided the response contains:
```json
{
"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'`
```json
{
"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
```python
@app.route("/character//", 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
```python
class RequestBodyModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: 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
```python
# Example 3: 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
```python
class RequestFormDataModel(BaseModel):
name: str
nickname: Optional[str]
# Example2: request body 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
```python
# 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 `validate` decorator
```python
@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.
```python
# 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](https://pydantic-docs.helpmanual.io/usage/model_config/#alias-generator) is natively supported for query and body models.
To use aliases in response modify response model
```python
def modify_key(text: str) -> str:
# do whatever you want with model keys
return text
class MyModel(BaseModel):
class Config:
alias_generator = modify_key
allow_population_by_field_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](https://github.com/bauerji/flask_pydantic/tree/master/example_app).
### 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
```bash
git clone https://github.com/bauerji/flask_pydantic.git
cd flask_pydantic
```
- create virtual environment and activate it
```bash
python3 -m venv venv
source venv/bin/activate
```
- install development requirements
```bash
python3 -m pip install -r requirements/test.pip
```
- checkout new branch and make your desired changes (don't forget to update tests)
```bash
git checkout -b
```
- run tests
```bash
python3 -m pytest
```
- if tests fails on Black tests, make sure You have your code compliant with style of [Black formatter](https://github.com/psf/black)
- push your changes and create a pull request to master branch
## TODOs:
- header request parameters
- cookie request parameters
%prep
%autosetup -n Flask-Pydantic-0.11.0
%build
%py3_build
%install
%py3_install
install -d -m755 %{buildroot}/%{_pkgdocdir}
if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
pushd %{buildroot}
if [ -d usr/lib ]; then
find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/lib64 ]; then
find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/bin ]; then
find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/sbin ]; then
find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
fi
touch doclist.lst
if [ -d usr/share/man ]; then
find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
fi
popd
mv %{buildroot}/filelist.lst .
mv %{buildroot}/doclist.lst .
%files -n python3-Flask-Pydantic -f filelist.lst
%dir %{python3_sitelib}/*
%files help -f doclist.lst
%{_docdir}/*
%changelog
* Tue Apr 11 2023 Python_Bot - 0.11.0-1
- Package Spec generated